Ver Mensaje Individual
  #36  
Antiguo 29-03-2013
Avatar de nlsgarcia
[nlsgarcia] nlsgarcia is offline
Miembro Premium
 
Registrado: feb 2007
Ubicación: Caracas, Venezuela
Posts: 2.206
Reputación: 23
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