Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Principal > Varios
Registrarse FAQ Miembros Calendario Guía de estilo Temas de Hoy

Grupo de Teaming del ClubDelphi

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #21  
Antiguo 25-03-2013
Avatar de Julián
Julián Julián is offline
Merodeador
 
Registrado: may 2003
Ubicación: en mi casa
Posts: 2.019
Poder: 10
Julián Va por buen camino
Lo mas optimo se supone que sería usar el gestlastline que estais comentando, pero en lugar de con un ttimer, con el evento del api de windows (el que te comenté en mi mensaje anterior) que se lanza sólo cuando se modifica el fichero.

Un saludo!
__________________
"la única iglesia que ilumina es la que arde"
Anonimo
Responder Con Cita
  #22  
Antiguo 25-03-2013
paquechu paquechu is offline
Miembro
 
Registrado: oct 2008
Posts: 51
Poder: 16
paquechu Va por buen camino
Hola Julian, coincido contigo totalmente.
Solo me queda una duda, que es donde falla el procedimiento que he adaptado basado en la funcion getlastline, y es la forma en la que se está generando el archivo de log.

Si al generar las líneas del archivo de log se hace un flush por cada línea generada, funciona OK, pero si el flush se hace cada ciertas líneas entonces no funciona bien. No se si este componente contemplaria este caso...

Saludos.

Cita:
Empezado por Julián Ver Mensaje
Lo mas optimo se supone que sería usar el gestlastline que estais comentando, pero en lugar de con un ttimer, con el evento del api de windows (el que te comenté en mi mensaje anterior) que se lanza sólo cuando se modifica el fichero.

Un saludo!
Responder Con Cita
  #23  
Antiguo 25-03-2013
Avatar de Julián
Julián Julián is offline
Merodeador
 
Registrado: may 2003
Ubicación: en mi casa
Posts: 2.019
Poder: 10
Julián Va por buen camino
Cita:
Empezado por paquechu Ver Mensaje
Hola Julian, coincido contigo totalmente.
Solo me queda una duda, que es donde falla el procedimiento que he adaptado basado en la funcion getlastline, y es la forma en la que se está generando el archivo de log.

Si al generar las líneas del archivo de log se hace un flush por cada línea generada, funciona OK, pero si el flush se hace cada ciertas líneas entonces no funciona bien. No se si este componente contemplaria este caso...

Pues yo entiendo que son dos cosas distintas:

1. Saber cuando determinado archivo *.log ha sido modificado.
2. leer la ultima(s) líneas

Para 1 puedes usarse un timer o el api de windows, y creo que hemos quedadi que lo mas elegante y correcto es el api de win.

Para 2 pues puedes echar mano de cualquier funcion de leer archivos que tenga el delphi, tal como indican algunos ejemplos que nos han puesto en este mismo hilo.

Lo que puedes hacer es poner aquí el cóodigo que te da error a ver que tal, pues con eso del flush no entiendo muy bien que es lo que te falla.

Un saludo!
__________________
"la única iglesia que ilumina es la que arde"
Anonimo
Responder Con Cita
  #24  
Antiguo 25-03-2013
paquechu paquechu is offline
Miembro
 
Registrado: oct 2008
Posts: 51
Poder: 16
paquechu Va por buen camino
Hola Julián,
Correcto, son dos cosas.
En cuanto a la primera mi duda es saber si tanto el timer como la api de windows se dan por enterados de que un archivo determinado se ha modificado o no desde que se lanza una instrucción write o solo se enteran en el momento en que se hace una instrucción flush del bufer a volcar a disco. (Esto tengo pendiente de probar en cuanto pueda).

En lo referente al segundo punto, pues no hay ninguna duda todo está claro.

En el mensaje #13 puse el codigo que estoy utilizadno, que no es que me de un error, sino que simplemente no funciona bien cuando desde el momento en que se hace un write para escribir el log si no se vuelca inmediatamente la línea a disco (con un flush) el código no se entera de que se ha enviado mas datos al archivo de log.

Aqui te pongo un enlace a la función flush a ver si se entiende mejor lo que quiero comentar :-) : http://www.delphibasics.co.uk/RTL.asp?Name=Flush

Cita:
Empezado por Julián Ver Mensaje
Pues yo entiendo que son dos cosas distintas:

1. Saber cuando determinado archivo *.log ha sido modificado.
2. leer la ultima(s) líneas

Para 1 puedes usarse un timer o el api de windows, y creo que hemos quedadi que lo mas elegante y correcto es el api de win.

Para 2 pues puedes echar mano de cualquier funcion de leer archivos que tenga el delphi, tal como indican algunos ejemplos que nos han puesto en este mismo hilo.

Lo que puedes hacer es poner aquí el cóodigo que te da error a ver que tal, pues con eso del flush no entiendo muy bien que es lo que te falla.

Un saludo!
Responder Con Cita
  #25  
Antiguo 25-03-2013
Avatar de mamcx
mamcx mamcx is offline
Moderador
 
Registrado: sep 2004
Ubicación: Medellín - Colombia
Posts: 3.913
Poder: 25
mamcx Tiene un aura espectacularmamcx Tiene un aura espectacularmamcx Tiene un aura espectacular
Cita:
Empezado por paquechu Ver Mensaje
Saber si tanto el timer como la api de windows se dan por enterados de que un archivo determinado se ha modificado o no desde que se lanza una instrucción write o solo se enteran en el momento en que se hace una instrucción flush del bufer a volcar a disco.

http://www.delphibasics.co.uk/RTL.asp?Name=Flush

Lee la descripcion de la ayuda. Medita sobre lo que dice. Alcanza la iluminación.
__________________
El malabarista.
Responder Con Cita
  #26  
Antiguo 25-03-2013
Avatar de ecfisa
ecfisa ecfisa is offline
Moderador
 
Registrado: dic 2005
Ubicación: Tres Arroyos, Argentina
Posts: 10.508
Poder: 36
ecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to behold
Cita:
Empezado por paquechu Ver Mensaje
...
COmo comentaba en mi anterior mensaje, el código que he utilizado se basa en tu funcion GetLastLine.
Ya veo que lo has mejorado.
...
Hola paquechu.

En realidad no mejoré la función, es la misma del mensaje #6 , sólo la integré a un TTimer para darle funcionalidad.

¿ Probaste el código del mensaje #17 y no te resultó ? Funcionó correctamente en todas mis pruebas, obteniendo en mi equipo, promedios de aprox. 850 microsegundos para conseguir la última de 100000 líneas.
Te adjunto el ejemplo debajo; la primera ejecución demorará un poco ya que creará un archivo con 100000 líneas de texto aleatorio en la carpeta donde lo ejecutes.

En cuanto a capturar la escritura en tiempo real, Delphi posee el componente TShellChangeNotifier que informa sobre las modificaciones sucedidas en determinada carpeta y de forma análoga podes obtenerlas usando API mediante la funcion SHChangeNotifyRegister.
Pero de ambos modos se notifica sobre un cambio del archivo como tál (creacion, borrado, etc) y no sobre una alteración en los datos internos del mismo. No quiero decir con esto que no es posible conseguir la información, sino que yo no pude encontrar el modo todavía.

Julián ya te puso un enlace y la sugerencia de utilizar un hook. Tal vez tengas que encarar ese punto por ese lado.

Saludos.
__________________
Daniel Didriksen

Guía de estilo - Uso de las etiquetas - La otra guía de estilo ....

Última edición por ecfisa fecha: 10-04-2013 a las 20:18:35.
Responder Con Cita
  #27  
Antiguo 26-03-2013
Avatar de Julián
Julián Julián is offline
Merodeador
 
Registrado: may 2003
Ubicación: en mi casa
Posts: 2.019
Poder: 10
Julián Va por buen camino
¡Eso que dice ecfisa es lo que yo decía: un hook!

Es que ya no me acordaba de esas cosas, pues hace como 8 años que no uso el puto Windows,
__________________
"la única iglesia que ilumina es la que arde"
Anonimo
Responder Con Cita
  #28  
Antiguo 26-03-2013
paquechu paquechu is offline
Miembro
 
Registrado: oct 2008
Posts: 51
Poder: 16
paquechu Va por buen camino
Buenas,

He probado el código que proponias, sin embargo al ejecutarlo sobre un archivo abierto de log se "queja" de que está abierto por otra aplicación y no puede continuar..... seguiré probando el resto de vuestras propuestas :-)
Saludos.

Cita:
Empezado por nlsgarcia Ver Mensaje
paquechu,


Un String en Delphi 7 (AnsiString, WideString) tiene un tamaño máximo de 2 GB, revisa este link: Limitation of TStringList.


EL código propuesto debería funcionar correctamente en archivos de texto que no sean de un tamaño notable (Logs rotativos), el ejemplo mencionado de 2.000.000 de lineas de texto fue solo a efectos de prueba de la función.

Te sugiero revisar detalladamente el código del Msg #6 (function GetLastLine), el código es altamente eficiente dado que solo obtiene la última línea del archivo de forma directa, lo cual la hace ideal como parte de un control TTimer y sin sobrecarga de recursos. Como comentario, funciono de forma optima con el archivo de pruebas mencionado.

Espero sea útil

Nelson.
Responder Con Cita
  #29  
Antiguo 26-03-2013
Avatar de ecfisa
ecfisa ecfisa is offline
Moderador
 
Registrado: dic 2005
Ubicación: Tres Arroyos, Argentina
Posts: 10.508
Poder: 36
ecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to behold
Cita:
Empezado por paquechu Ver Mensaje
Buenas,

He probado el código que proponias, sin embargo al ejecutarlo sobre un archivo abierto de log se "queja" de que está abierto por otra aplicación y no puede continuar
Hola paquechu.

Muy probablemente tengas ese problema con cualquiera de las sugerencias de este hilo, eso es a lo que me refería en el mensaje (#4).

Saludos.
__________________
Daniel Didriksen

Guía de estilo - Uso de las etiquetas - La otra guía de estilo ....
Responder Con Cita
  #30  
Antiguo 26-03-2013
paquechu paquechu is offline
Miembro
 
Registrado: oct 2008
Posts: 51
Poder: 16
paquechu Va por buen camino
Si, eso es, pero se soluciona añadiendo el flag fmShareDenyNone en el momento de abrir el archivo
Saludos. :-)

Cita:
Empezado por ecfisa Ver Mensaje
Hola paquechu.

Muy probablemente tengas ese problema con cualquiera de las sugerencias de este hilo, eso es a lo que me refería en el mensaje (#4).

Saludos.
Responder Con Cita
  #31  
Antiguo 26-03-2013
paquechu paquechu is offline
Miembro
 
Registrado: oct 2008
Posts: 51
Poder: 16
paquechu Va por buen camino
Ecfisa,
Por otro lado he probado el codigo que pones en el mensaje #17, pero tampoco contempla todas las líneas añadidas desde la última modificación y se "traga" algunas... sigo pensando que es lo del flush, je,je
Gracias :-)

EDITO: Otra cosa mas.... he intentado descargarme el ejemplo del mensaje #26 y no puedo, no se si será por el control de contenido de mi trabajo, pero al abrir el zip se queja de error crc (lo he intentado varias veces. lo probaré desde casa)....

Última edición por paquechu fecha: 26-03-2013 a las 12:52:29.
Responder Con Cita
  #32  
Antiguo 26-03-2013
Avatar de ecfisa
ecfisa ecfisa is offline
Moderador
 
Registrado: dic 2005
Ubicación: Tres Arroyos, Argentina
Posts: 10.508
Poder: 36
ecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to behold
Cita:
Empezado por paquechu Ver Mensaje
Si, eso es, pero se soluciona añadiendo el flag fmShareDenyNone en el momento de abrir el archivo
Saludos. :-)
Si, es correcto, pero te lo señalaba por el comentario de tu mensaje anterior (#28).

Saludos.
__________________
Daniel Didriksen

Guía de estilo - Uso de las etiquetas - La otra guía de estilo ....
Responder Con Cita
  #33  
Antiguo 26-03-2013
Avatar de ecfisa
ecfisa ecfisa is offline
Moderador
 
Registrado: dic 2005
Ubicación: Tres Arroyos, Argentina
Posts: 10.508
Poder: 36
ecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to beholdecfisa is a splendid one to behold
Cita:
Empezado por paquechu Ver Mensaje
Ecfisa,
Por otro lado he probado el codigo que pones en el mensaje #17, pero tampoco contempla todas las líneas añadidas desde la última modificación y se "traga" algunas... sigo pensando que es lo del flush, je,je
Gracias :-)
Es que el código que te sugerí no hace eso. El código devuelve la ultima línea añadida, no el conjunto de líneas agregadas desde desde la última modificacion.

Saludos.
__________________
Daniel Didriksen

Guía de estilo - Uso de las etiquetas - La otra guía de estilo ....
Responder Con Cita
  #34  
Antiguo 26-03-2013
paquechu paquechu is offline
Miembro
 
Registrado: oct 2008
Posts: 51
Poder: 16
paquechu Va por buen camino
Acabo de hacer pruebas con el "notificador" que tiene la api de windows y creo que tampoco soluciona el problema de los cambios sufridos por un archivo en tiempo real.

He encontrado un archivo donde figura el componente y un ejemplo de uso que he probado de la siguiente forma:

En mi PC: he ejecutado el programa de demo que incluye el archivo que os dejo en el post y cuando hay una modificacion en un archivo del directorio que se elija, el cambio de tamaño de archivo se refleja bien, pero cuando hago lo mismo en el servidor donde se genera el log del isa server (que es un servidor virtual) ya no parece enterarse de los cambios.

NO se, creo que voy a investigar mas en la linea de guardar la posicion de la ultima línea leida y la próxima vez que el codigo detecte modificación leer a partir de esa posición.....

Saludos. :-)
Archivos Adjuntos
Tipo de Archivo: zip dirwatch.zip (7,6 KB, 3 visitas)

Última edición por paquechu fecha: 26-03-2013 a las 15:32:46. Razón: Se me ha olvidado el archivo....
Responder Con Cita
  #35  
Antiguo 27-03-2013
Avatar de nlsgarcia
[nlsgarcia] nlsgarcia is offline
Miembro Premium
 
Registrado: feb 2007
Ubicación: Caracas, Venezuela
Posts: 2.206
Poder: 21
nlsgarcia Tiene un aura espectacularnlsgarcia Tiene un aura espectacular
paquechu,

Cita:
Empezado por paquechu
...Acabo de hacer pruebas con el "notificador" que tiene la api de windows y creo que tampoco soluciona el problema de los cambios sufridos por un archivo en tiempo real...
Revisa este código:
Código Delphi [-]
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons, FileCtrl;

type
  TForm1 = class(TForm)
    BitBtn1: TBitBtn;
    BitBtn2: TBitBtn;
    Label1: TLabel;
    ListBox1: TListBox;
    procedure BitBtn1Click(Sender: TObject);
    procedure BitBtn2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TDirectoryMonitor = class(TThread)
  public
    DirectoryToMonitor : String;
    MsgMonitor : String;
  protected
    procedure StatusOn;
    procedure StatusOff;
    procedure MsgError;
    procedure EventMonitor;
    procedure Execute; override;
  end;

var
  Form1: TForm1;
  DirectoryMonitor : TDirectoryMonitor;

implementation

{$R *.dfm}

procedure TDirectoryMonitor.StatusOn;
begin
   with form1 do
   begin
      label1.Font.Color := clBlue;
      Label1.Caption := 'Thread On';
   end;
end;

procedure TDirectoryMonitor.StatusOff;
begin
   with form1 do
   begin
      label1.Font.Color := clRed;
      Label1.Caption := 'Thread Off';
   end;
end;

procedure TDirectoryMonitor.MsgError;
begin
   MessageDlg('Error en la Ruta de Monitoreo', mtInformation, [mbOK],0);
end;

procedure TDirectoryMonitor.EventMonitor;
begin
   with Form1 do
   begin
      ListBox1.Items.Add(DirectoryMonitor.MsgMonitor);
      if ListBox1.ScrollWidth < ListBox1.Canvas.TextWidth(DirectoryMonitor.MsgMonitor) then
         ListBox1.ScrollWidth := ListBox1.Canvas.TextWidth(DirectoryMonitor.MsgMonitor) + 120;
   end;
end;

procedure TDirectoryMonitor.Execute;
type
   PFileNotifyInformation = ^TFileNotifyInformation;

   TFileNotifyInformation = record
      NextEntryOffset: DWORD;
      Action: DWORD;
      FileNameLength: DWORD;
      FileName: WideChar;
   end;

const
   FILE_LIST_DIRECTORY = 1;
   BufferLength = 65536;

var
   H: THandle;
   fDirHandle: THandle;
   fChangeHandle: THandle;
   Filter, BytesRead: DWORD;
   Offset, NextOffset: DWORD;
   Buffer: array[0..BufferLength - 1] of byte;
   Overlap: TOverlapped;
   WaitResult: DWORD;
   FileName : String;
   InfoPointer: PFileNotifyInformation;
   Action : string;
   Attrs : Integer;

begin

   FreeOnTerminate := True;

   fDirHandle := CreateFile(PChar(DirectoryToMonitor),
                            FILE_LIST_DIRECTORY or GENERIC_READ,
                            FILE_SHARE_READ or FILE_SHARE_WRITE or
                            FILE_SHARE_DELETE, nil, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS or
                            FILE_FLAG_OVERLAPPED, 0);

   if (fDirHandle = INVALID_HANDLE_VALUE) or (FileExists(DirectoryToMonitor)) then
   begin
      Synchronize(MsgError);
      Exit;
   end;

   fChangeHandle := CreateEvent(nil, FALSE, FALSE, nil);

   FillChar(Overlap, SizeOf(TOverlapped), 0);

   Overlap.hEvent := fChangeHandle;

   Filter := FILE_NOTIFY_CHANGE_FILE_NAME or FILE_NOTIFY_CHANGE_DIR_NAME
             or FILE_NOTIFY_CHANGE_SIZE or FILE_NOTIFY_CHANGE_LAST_WRITE;

   Synchronize(StatusOn);

   while not Terminated do
   begin
      if ReadDirectoryChangesW(fDirHandle, @Buffer[0], BufferLength, TRUE, Filter,
                              @BytesRead, @Overlap, nil)
      then
      begin

         WaitResult := WaitForMultipleObjects(1, @fChangeHandle, FALSE, 100);
         if (WaitResult = WAIT_OBJECT_0) and (WaitResult <> WAIT_TIMEOUT) then
         begin

            InfoPointer := @Buffer[0];

            repeat

               NextOffset := InfoPointer.NextEntryOffset;
               FileName := WideCharToString(@InfoPointer.FileName);

               Attrs := FileGetAttr(DirectoryToMonitor + '\' + FileName);

               if Attrs and faDirectory = faDirectory then
                  Action := 'DIRECTORY_';

               if Attrs and faArchive = faArchive then
                  Action := 'FILE_';

               if InfoPointer.Action = 1 then Action :=  Action + 'ACTION_ADDED';
               if InfoPointer.Action = 2 then Action :=  'FILE_DIRECTORY_ACTION_REMOVED';
               if InfoPointer.Action = 3 then Action :=  Action + 'ACTION_MODIFIED';
               if InfoPointer.Action = 4 then Action :=  Action + 'ACTION_RENAMED_OLD_NAME';
               if InfoPointer.Action = 5 then Action :=  Action + 'ACTION_RENAMED_NEW_NAME';

               MsgMonitor := Action + ': ' + FileName;

               PByte(InfoPointer) := PByte(DWORD(InfoPointer) + NextOffset);

            until NextOffset = 0;

            FillChar(Buffer, SizeOf(Buffer), 0);
            Synchronize(EventMonitor);

         end;
      end
      else
      begin
         Break;
      end;
   end;

   if fChangeHandle <> 0 then
      CloseHandle(fChangeHandle);

   if fDirHandle <> INVALID_HANDLE_VALUE then
      CloseHandle(fDirHandle);

   Synchronize(StatusOff);

end;

procedure TForm1.BitBtn1Click(Sender: TObject);
var
   Directory : String;
begin
   if SelectDirectory('Selección de Directorio para Monitoreo', 'C:\', Directory) then
   begin
      ListBox1.Clear;
      DirectoryMonitor := TDirectoryMonitor.Create(True);
      DirectoryMonitor.DirectoryToMonitor := Directory;
      DirectoryMonitor.Resume;
      BitBtn1.Enabled := False;
   end
   else
      MessageDlg('No fue Seleccionado Ningún Directorio para Monitoreo', mtInformation, [mbOK],0);
end;

procedure TForm1.BitBtn2Click(Sender: TObject);
begin
   if Assigned(DirectoryMonitor) then
   begin
      DirectoryMonitor.Terminate;
      BitBtn1.Enabled := True;
   end

end;

procedure TForm1.FormCreate(Sender: TObject);
begin
   label1.Font.Color := clRed;
   Label1.Caption := 'Thread Off';
end;

end.
El código anterior permite detectar cambios a nivel de Creación, Modificación y Eliminación de Archivos y Directorios dentro de un Directorio específico (Directorio objetivo de Monitoreo) y sus subdirectorios por medio de la función ReadDirectoryChangesW.

Este código fue probado en tres Máquinas Virtuales con Windows XP Professional x32, Windows 7 Professional x32 y x64 y una Máquina Física con Windows 7 Professional x32 y en todos los casos funcionó correctamente.

El código esta disponible en el siguiente link: http://terawiki.clubdelphi.com/Delph...oryMonitor.rar

Nota: Una forma simple de probar el programa es monitorear el directorio C:\

Revisa estos links:
Cita:
1- ReadDirectoryChangesW function : http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx

2- WaitForMultipleObjects function : http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx

3- CreateFile function : http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx
Espero sea útil

Nelson.

Última edición por nlsgarcia fecha: 27-03-2013 a las 04:08:25.
Responder Con Cita
  #36  
Antiguo 29-03-2013
Avatar de nlsgarcia
[nlsgarcia] nlsgarcia is offline
Miembro Premium
 
Registrado: feb 2007
Ubicación: Caracas, Venezuela
Posts: 2.206
Poder: 21
nlsgarcia Tiene un aura espectacularnlsgarcia Tiene un aura espectacular
paquechu,

Cita:
Empezado por mamcx
...Seria bueno almacenar cuantas lineas habian en la lectura anterior y luego restarlas...
Cita:
Empezado por Julián
...usar el api de windows, que permite colgar un hook o como se llame que "avisa" cada vez que se modifique el archivo...
Cita:
Empezado por paquechu
...voy a investigar mas en la linea de guardar la posicion de la ultima línea leida y la próxima vez que el codigo detecte modificación leer a partir de esa posición...
Revisa este código:
Código Delphi [-]
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons, FileCtrl, StrUtils;

type
  TForm1 = class(TForm)
    BitBtn1: TBitBtn;
    BitBtn2: TBitBtn;
    Label1: TLabel;
    ListBox1: TListBox;
    BitBtn3: TBitBtn;
    procedure BitBtn1Click(Sender: TObject);
    procedure BitBtn2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure BitBtn3Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TDirectoryMonitor = class(TThread)
  public
    DirectoryToMonitor : String;
    FileToMonitor : String;
    MsgMonitor : String;
    MsgError : String;
  protected
    procedure StatusOn;
    procedure StatusOff;
    procedure DspMsgError;
    procedure EventMonitor;
    function GetFileLastLine(const FileName: string): string;
    procedure Execute; override;
  end;

var
  Form1: TForm1;
  DirectoryMonitor : TDirectoryMonitor;

implementation

{$R *.dfm}

// Establece el mensaje de Hilo en On.
procedure TDirectoryMonitor.StatusOn;
begin
   with form1 do
   begin
      label1.Font.Color := clBlue;
      Label1.Caption := 'Thread On';
   end;
end;

// Establece el mensaje de Hilo en Off.
procedure TDirectoryMonitor.StatusOff;
begin
   with form1 do
   begin
      label1.Font.Color := clRed;
      Label1.Caption := 'Thread Off';
   end;
end;

// Visualiza mensages de error.
procedure TDirectoryMonitor.DspMsgError;
begin
   MessageDlg(MsgError, mtInformation, [mbOK],0);
end;

// Muestra en un control TListBox los cambios registrados en el archivo monitoreado.
procedure TDirectoryMonitor.EventMonitor;
var
   Data : String;
   i,p : Integer;
   offset : Integer;

begin
   with Form1 do
   begin

      offset := 1;
      Data := DirectoryMonitor.MsgMonitor;
      for i := 1 to Length(Data) do
      begin
         if  Data[i] = Chr(10) then
         begin
            p := PosEx(Chr(10),Data,offset);
            ListBox1.Items.Add(Copy(Data,offset,p-offset));
            if ListBox1.ScrollWidth < ListBox1.Canvas.TextWidth(DirectoryMonitor.MsgMonitor) then
               ListBox1.ScrollWidth := ListBox1.Canvas.TextWidth(DirectoryMonitor.MsgMonitor) + 120;
            offset := p+1;
         end;
      end;

   end;
end;

// Lee las n líneas adicionados a un archivo desde un valor de control de Monitoreo inicial
// El valor de control inicial es el tamaño del archivo el cual se establece al momento
// de la llamada al hilo de monitoreo.
function TDirectoryMonitor.GetFileLastLine(const FileName: string): string;
var
   F1  : TFileStream;
   F2  : TFileStream;
   Buffer : string;
   BufChar : Char;
   i, pi, pf   : Integer;
   LastSize : LongWord;
   Err : Integer;
   FileControl : String;

begin
   try

      FileControl := ExtractFilePath(Application.ExeName) + 'FileControl.txt';

      F1 := TFileStream.Create(FileControl, fmOpenReadWrite or fmShareDenyNone);
      SetLength(Buffer, F1.Size);
      F1.Read(Buffer[1],F1.size);
      Val(Buffer, LastSize,Err);

      F2 := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);

      pi := -1;
      pf := LastSize - F2.Size;

      Result := '';

      // Se adicionaron n líneas al archivo monitoreado por 1 ocurrencia
      if (LastSize = 0) then
      begin

         pf := 0;
         F2.Seek(0,soFromBeginning);
         while F2.Position < F2.Size do
         begin
            F2.Read(BufChar, 1);
            Result := Result + BufChar;
         end;

         Buffer := Format('%.10d',[F2.Size]);
         F1.Seek(0,soFromBeginning);
         // Actualiza el valor de control de Monitoreo
         F1.Write(Buffer[1],Length(Buffer));

      end;

      // Se adicionaron n líneas al archivo monitoreado por n ocurrencia
      if (pf < 0) then
      begin

         pf := pf - 1;

         repeat
            F2.Seek(pi, soFromEnd);
            F2.Read(BufChar, SizeOf(BufChar));
            Insert(BufChar, Result, 1);
            Dec(pi);
         until (pi = pf);

         Buffer := Format('%.10d',[F2.Size]);
         F1.Seek(0,soFromBeginning);
         // Actualiza el valor de control de Monitoreo
         F1.Write(Buffer[1],Length(Buffer));

      end;

   finally

      F1.Free;
      F2.Free;

   end;
end;

// Ejecuta el Hilo de Monitoreo a un archivo específico.
procedure TDirectoryMonitor.Execute;
type
   PFileNotifyInformation = ^TFileNotifyInformation;

   TFileNotifyInformation = record
      NextEntryOffset: DWORD;
      Action: DWORD;
      FileNameLength: DWORD;
      FileName: WideChar;
   end;

const
   FILE_LIST_DIRECTORY = 1;
   BufferLength = 65536;

var
   H: THandle;
   fDirHandle: THandle;
   fChangeHandle: THandle;
   Filter, BytesRead: DWORD;
   Offset, NextOffset: DWORD;
   Buffer: array[0..BufferLength - 1] of byte;
   Overlap: TOverlapped;
   WaitResult: DWORD;
   FileName : String;
   InfoPointer: PFileNotifyInformation;
   Action : string;
   Attrs : Integer;
   DateLastLine : TDateTime;

begin

   FreeOnTerminate := True;

   fDirHandle := CreateFile(PChar(DirectoryToMonitor),
                            FILE_LIST_DIRECTORY or GENERIC_READ,
                            FILE_SHARE_READ or FILE_SHARE_WRITE or
                            FILE_SHARE_DELETE, nil, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS or
                            FILE_FLAG_OVERLAPPED, 0);

   if (fDirHandle = INVALID_HANDLE_VALUE) or (FileExists(DirectoryToMonitor)) then
   begin
      MsgError := 'Función CreateFile: Error en la Ruta de Monitoreo';
      Synchronize(DspMsgError);
      Exit;
   end;

   fChangeHandle := CreateEvent(nil, FALSE, FALSE, nil);

   FillChar(Overlap, SizeOf(TOverlapped), 0);

   Overlap.hEvent := fChangeHandle;

   Filter := FILE_NOTIFY_CHANGE_FILE_NAME or FILE_NOTIFY_CHANGE_DIR_NAME
             or FILE_NOTIFY_CHANGE_SIZE or FILE_NOTIFY_CHANGE_LAST_WRITE;

   Synchronize(StatusOn);

   while not Terminated do
   begin
      if not FileExists(DirectoryToMonitor + '\' + FiletoMonitor) then
      begin
         MsgError := 'Error en el Archivo o Directorio de Monitoreo Establecido Inicialmente';
         Synchronize(DspMsgError);
         Exit;
      end;

      if ReadDirectoryChangesW(fDirHandle, @Buffer[0], BufferLength, TRUE, Filter,
                              @BytesRead, @Overlap, nil)
      then
      begin

         WaitResult := WaitForMultipleObjects(1, @fChangeHandle, FALSE, 100);
         if (WaitResult = WAIT_OBJECT_0) and (WaitResult <> WAIT_TIMEOUT) then
         begin

            InfoPointer := @Buffer[0];

            repeat

               NextOffset := InfoPointer.NextEntryOffset;
               FileName := WideCharToString(@InfoPointer.FileName);

               if (FileToMonitor = FileName) and (InfoPointer.Action = 3) then
               begin
                  Attrs := FileGetAttr(DirectoryToMonitor + '\' + FileName);
                  if (Attrs and faArchive = faArchive) then
                  begin
                     MsgMonitor := GetFileLastLine(FileToMonitor);
                     FillChar(Buffer, SizeOf(Buffer), 0);
                     Synchronize(EventMonitor);
                  end;
               end;

               PByte(InfoPointer) := PByte(DWORD(InfoPointer) + NextOffset);

            until NextOffset = 0;
         end;
      end
      else
      begin
         Break;
      end;
   end;

   if fChangeHandle <> 0 then
      CloseHandle(fChangeHandle);

   if fDirHandle <> INVALID_HANDLE_VALUE then
      CloseHandle(fDirHandle);

   Synchronize(StatusOff);

end;

// Inicia la función de monitoreo a un archivo específico
procedure TForm1.BitBtn1Click(Sender: TObject);
var
   Directory : String;
   openDialog : TOpenDialog;
   F1  : TFileStream;
   F2  : TFileStream;
   Buffer : String;
   FileControl : String;

begin

   openDialog := TOpenDialog.Create(self);
   openDialog.InitialDir := GetCurrentDir;
   openDialog.Options := [ofFileMustExist];
   openDialog.Filter := 'Archivo a Monitorer|*.txt';
   openDialog.FilterIndex := 1;

   if openDialog.Execute then
   begin

      try

         FileControl := ExtractFilePath(Application.ExeName) + 'FileControl.txt';
         F1 := TFileStream.Create(FileControl, fmCreate);
         F2 := TFileStream.Create(openDialog.FileName, fmOpenRead or fmShareDenyNone);
         Buffer := Format('%.10d',[F2.Size]);
         // Establece el valor de control de monitoreo inicial
         F1.Write(Buffer[1],Length(Buffer));

         ListBox1.Clear;
         DirectoryMonitor := TDirectoryMonitor.Create(True);
         DirectoryMonitor.DirectoryToMonitor := ExtractFileDir(openDialog.FileName);
         DirectoryMonitor.FileToMonitor := ExtractFileName(openDialog.FileName);
         DirectoryMonitor.Resume;
         BitBtn1.Enabled := False;

      finally

         F1.Free;
         F2.Free;

      end;

   end
   else
      MessageDlg('No fue Seleccionado Ningún Archivo para Monitoreo', mtInformation, [mbOK],0);

   openDialog.Free;

end;

// Finaliza la función de monitoreo a un archivo específico
procedure TForm1.BitBtn2Click(Sender: TObject);
begin
   if Assigned(DirectoryMonitor) then
   begin
      DirectoryMonitor.Terminate;
      BitBtn1.Enabled := True;
   end

end;

// Valores iniciales
procedure TForm1.FormCreate(Sender: TObject);
begin
   label1.Font.Color := clRed;
   Label1.Caption := 'Thread Off';
end;

// Salvar los cambios registrados durante el monitoreo a un archivo de texto.
procedure TForm1.BitBtn3Click(Sender: TObject);
var
   saveDialog : TSaveDialog; 
   StrList : TStringList;

begin

   saveDialog := TSaveDialog.Create(self);
   saveDialog.Title := 'Save Monitor File Changes';
   saveDialog.InitialDir := ExtractFileDir(Application.ExeName);
   saveDialog.Filter := 'Text file|*.txt';
   saveDialog.DefaultExt := 'txt';
   saveDialog.FilterIndex := 1;
   saveDialog.FileName := 'Monitor_File.txt';

   if saveDialog.Execute then
   begin
      try
         if Assigned(DirectoryMonitor) then
         begin
            DirectoryMonitor.Terminate;
            BitBtn1.Enabled := True;
         end;
         StrList:= TStringList.Create;
         StrList.Assign(ListBox1.Items);
         StrList.SaveToFile(saveDialog.FileName);
         MessageDlg('El Archivo de Monitoreo Fue Salvado', mtInformation, [mbOK],0)
      finally
         StrList.Free
      end;

   end
   else
      MessageDlg('Cancelado el Backup del Archivo de Monitoreo', mtInformation, [mbOK],0);

   saveDialog.Free;

end;

end.
El código anterior permite monitorear un archivo específico (Función ReadDirectoryChangesW) y por medio de un archivo de control determinar las líneas que se han adicionado durante el monitoreo. El código es una especialización de lo sugerido en los Msg #6 y #35.

Nota: El código anterior asume el monitoreo de archivos de texto en el cual las lineas finalizan con los caracteres de control CRLF. Solo se puede monitorear un archivo a la vez en esta versión la cual permite:

1- Iniciar el Hilo de Monitoreo a un Archivo Específico.

2- Finalizar el Hilo de Monitoreo a un Archivo Específico.

3- Salvar los cambios registrados en el Archivo Monitoreado a un archivo de texto.

4- Solo se monitorean las líneas adicionadas.

5- El archivo de control es creado cada vez que se selecciona un archivo específico para monitoreo.

El código esta disponible en el siguiente link: http://terawiki.clubdelphi.com/Delph...ileMonitor.rar

Espero sea útil

Nelson.

Última edición por nlsgarcia fecha: 29-03-2013 a las 22:59:14.
Responder Con Cita
  #37  
Antiguo 29-03-2013
Avatar de nlsgarcia
[nlsgarcia] nlsgarcia is offline
Miembro Premium
 
Registrado: feb 2007
Ubicación: Caracas, Venezuela
Posts: 2.206
Poder: 21
nlsgarcia Tiene un aura espectacularnlsgarcia Tiene un aura espectacular
paquechu,

Nota: La función GetFileLastLine del código del Msg #36 se modifico para adaptarse a posibles operaciones de Delete en el archivo monitoreado por medio de la Actualización del Valor de Control (Tamaño del archivo monitoreado), como se muestra a continuación:

Código Delphi [-]
// Lee las n líneas adicionados a un archivo desde un valor de control de Monitoreo inicial
// El valor de control inicial es el tamaño del archivo el cual se establece al momento
// de la llamada al hilo de monitoreo.
function TDirectoryMonitor.GetFileLastLine(const FileName: string): string;
var
   F1  : TFileStream;
   F2  : TFileStream;
   Buffer : string;
   BufChar : Char;
   i, pi, pf   : Integer;
   LastSize : LongWord;
   Err : Integer;
   FileControl : String;

begin
   try

      FileControl := ExtractFilePath(Application.ExeName) + 'FileControl.txt';

      F1 := TFileStream.Create(FileControl, fmOpenReadWrite or fmShareDenyNone);
      SetLength(Buffer, F1.Size);
      F1.Read(Buffer[1],F1.size);
      Val(Buffer, LastSize,Err);

      F2 := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);

      pi := -1;
      pf := LastSize - F2.Size;

      Result := '';

      // Se adicionaron n líneas al archivo monitoreado por 1 ocurrencia
      if (LastSize = 0) then
      begin

         pf := 0;
         F2.Seek(0,soFromBeginning);
         while F2.Position < F2.Size do
         begin
            F2.Read(BufChar, 1);
            Result := Result + BufChar;
         end;

         Buffer := Format('%.10d',[F2.Size]);
         F1.Seek(0,soFromBeginning);
         // Actualiza el valor de control de Monitoreo
         F1.Write(Buffer[1],Length(Buffer));

      end;

      // Se adicionaron n líneas al archivo monitoreado por n ocurrencia
      if (pf < 0) then
      begin

         pf := pf - 1;

         repeat
            F2.Seek(pi, soFromEnd);
            F2.Read(BufChar, SizeOf(BufChar));
            Insert(BufChar, Result, 1);
            Dec(pi);
         until (pi = pf);

         Buffer := Format('%.10d',[F2.Size]);
         F1.Seek(0,soFromBeginning);
         // Actualiza el valor de control de Monitoreo
         F1.Write(Buffer[1],Length(Buffer));

      end;

      // El tamaño del archivo monitoreado disminuyo, se ajusta el valor de control.
      if (pf > 0) then
      begin

         Buffer := Format('%.10d',[F2.Size]);
         F1.Seek(0,soFromBeginning);
         // Actualiza el valor de control de Monitoreo
         F1.Write(Buffer[1],Length(Buffer));

      end;

   finally

      F1.Free;
      F2.Free;

   end;
end;
Esta modificación solo ajusta el valor de control en el caso de que el archivo monitoreado disminuya su tamaño, no se registra el cambio en el control TListBox.

El nuevo código modificado esta disponible en el link: http://terawiki.clubdelphi.com/Delph...i%F3n+2%29.rar

Espero sea útil

Nelson.

Última edición por nlsgarcia fecha: 29-03-2013 a las 23:57:19.
Responder Con Cita
  #38  
Antiguo 01-04-2013
paquechu paquechu is offline
Miembro
 
Registrado: oct 2008
Posts: 51
Poder: 16
paquechu Va por buen camino
Hola nlsgarcia,
He probado el código y funciona a la perfección.... lee las líneas correctamente, se añadan una a una o varias de golpe.
Gran curro :-)
Muchisimas gracias por vuestra gran ayuda :-)
Un abrazo.
Responder Con Cita
Respuesta



Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro


La franja horaria es GMT +2. Ahora son las 13:58:38.


Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi
Copyright 1996-2007 Club Delphi