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
public
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}
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.DspMsgError;
begin
MessageDlg(MsgError, mtInformation, [mbOK],0);
end;
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;
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 := '';
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);
F1.Write(Buffer[1],Length(Buffer));
end;
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);
F1.Write(Buffer[1],Length(Buffer));
end;
finally
F1.Free;
F2.Free;
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;
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;
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]);
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;
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;
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.