{ ############################################################################ } { # # } { # MirandaNG HistoryToDB Plugin v2.5 # } { # # } { # License: GPLv3 # } { # # } { # Author: Grigorev Michael (icq: 161867489, email: sleuthhound@gmail.com) # } { # # } { ############################################################################ } unit FSMonitor; {$I jedi.inc} interface type // ��������� � ����������� �� ��������� � �������� ������� (���������� � callback ���������) PInfoCallBack = ^TInfoCallBack; TInfoCallBack = record FAction : Integer; // ��� ��������� (��������� FILE_ACTION_XXX) FDrive : String; // ����, �� ������� ���� ��������� FOldFileName : String; // ��� ����� �� �������������� FNewFileName : String; // ��� ����� ����� �������������� end; // callback ���������, ���������� ��� ��������� � �������� ������� TWatchFileSystemCallBack = procedure (pInfo: TInfoCallBack); { ������ ����������� �������� ������� ��������: pName - ��� ����� ��� ����������� pFilter - ���������� �������� FILE_NOTIFY_XXX pSubTree - ���������� �� ��� �������� �������� ����� pInfoCallBack - ����� callback ���������, ���������� ��� ��������� � �������� ������� } procedure StartWatch(pName: String; pFilter: Cardinal; pSubTree: Boolean; pInfoCallBack: TWatchFileSystemCallBack); // ��������� ����������� procedure StopWatch; implementation uses Classes, Windows, SysUtils, IniFiles; const FILE_LIST_DIRECTORY = $0001; type PFileNotifyInformation = ^TFileNotifyInformation; TFileNotifyInformation = record NextEntryOffset : DWORD; Action : DWORD; FileNameLength : DWORD; FileName : Array[0..0] of WideChar; end; WFSError = class(Exception); TWFS = class(TThread) private FName : String; FFilter : Cardinal; FSubTree : Boolean; FInfoCallBack : TWatchFileSystemCallBack; FWatchHandle : THandle; FWatchBuf : Array[0..4096] of Byte; FOverLapp : TOverlapped; FPOverLapp : POverlapped; FBytesWritte : DWORD; FCompletionPort : THandle; FNumBytes : Cardinal; FOldFileName : String; function CreateDirHandle(aDir: string): THandle; procedure WatchEvent; //procedure HandleEvent; protected procedure Execute; override; public constructor Create(pName: String; pFilter: Cardinal; pSubTree: Boolean; pInfoCallBack: TWatchFileSystemCallBack); destructor Destroy; override; end; var WFS : TWFS; procedure StartWatch(pName: String; pFilter: Cardinal; pSubTree: Boolean; pInfoCallBack: TWatchFileSystemCallBack); begin WFS := TWFS.Create(pName, pFilter, pSubTree, pInfoCallBack); end; procedure StopWatch; begin if Assigned(WFS) then begin PostQueuedCompletionStatus(WFS.FCompletionPort, 0, 0, nil); WFS.Terminate; WFS.WaitFor; WFS.Free; WFS := nil; end; end; constructor TWFS.Create(pName: String; pFilter: Cardinal; pSubTree: Boolean; pInfoCallBack: TWatchFileSystemCallBack); begin inherited Create(True); FreeOnTerminate := False; FName := IncludeTrailingBackslash(pName); FFilter := pFilter; FSubTree := pSubTree; FOldFileName := EmptyStr; ZeroMemory(@FOverLapp, SizeOf(TOverLapped)); FPOverLapp := @FOverLapp; ZeroMemory(@FWatchBuf, SizeOf(FWatchBuf)); FInfoCallBack := pInfoCallBack; Resume; end; destructor TWFS.Destroy; begin PostQueuedCompletionStatus(FCompletionPort, 0, 0, nil); CloseHandle(FWatchHandle); FWatchHandle := 0; CloseHandle(FCompletionPort); FCompletionPort := 0; inherited Destroy; end; function TWFS.CreateDirHandle(aDir: String): THandle; begin Result := CreateFile(PChar(aDir), FILE_LIST_DIRECTORY, FILE_SHARE_READ+FILE_SHARE_DELETE+FILE_SHARE_WRITE, nil,OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS or FILE_FLAG_OVERLAPPED, 0); end; procedure TWFS.Execute; begin FWatchHandle := CreateDirHandle(FName); WatchEvent; end; {procedure TWFS.HandleEvent; var FileNotifyInfo : PFileNotifyInformation; InfoCallBack : TInfoCallBack; Offset : Longint; begin Pointer(FileNotifyInfo) := @FWatchBuf[0]; repeat Offset := FileNotifyInfo^.NextEntryOffset; InfoCallBack.FAction := FileNotifyInfo^.Action; InfoCallBack.FDrive := FName; SetString(InfoCallBack.FNewFileName,FileNotifyInfo^.FileName,FileNotifyInfo^.FileNameLength ); InfoCallBack.FNewFileName := Trim(InfoCallBack.FNewFileName); case FileNotifyInfo^.Action of FILE_ACTION_RENAMED_OLD_NAME: FOldFileName := Trim(WideCharToString(@(FileNotifyInfo^.FileName[0]))); FILE_ACTION_RENAMED_NEW_NAME: InfoCallBack.FOldFileName := FOldFileName; end; FInfoCallBack(InfoCallBack); PChar(FileNotifyInfo) := PChar(FileNotifyInfo)+Offset; until (Offset=0) or Terminated; end;} procedure TWFS.WatchEvent; var {$ifdef DELPHIXE_UP} CompletionKey: DWORD_PTR; {$ELSE} CompletionKey: Cardinal; {$endif} // ���������� �� HandleEvent FileNotifyInfo : PFileNotifyInformation; InfoCallBack : TInfoCallBack; Offset : Longint; // End begin FCompletionPort := CreateIoCompletionPort(FWatchHandle, 0, Longint(pointer(self)), 0); ZeroMemory(@FWatchBuf, SizeOf(FWatchBuf)); if not ReadDirectoryChanges(FWatchHandle, @FWatchBuf, SizeOf(FWatchBuf), FSubTree, FFilter, @FBytesWritte, @FOverLapp, 0) then begin raise WFSError.Create(SysErrorMessage(GetLastError)); Terminate; end else begin while not Terminated do begin GetQueuedCompletionStatus(FCompletionPort, FNumBytes, CompletionKey, FPOverLapp, INFINITE); if CompletionKey <> 0 then begin //Synchronize(HandleEvent); { �.�. Synchronize(HandleEvent) �� ������������ � ������� �� ���������� �������, �� ��� ��� ��������� ���� ��������� ��� -> http://forum.qip.ru/showthread.php?t=25200 } Pointer(FileNotifyInfo) := @FWatchBuf[0]; repeat Offset := FileNotifyInfo^.NextEntryOffset; InfoCallBack.FAction := FileNotifyInfo^.Action; InfoCallBack.FDrive := FName; SetString(InfoCallBack.FNewFileName,FileNotifyInfo^.FileName,FileNotifyInfo^.FileNameLength ); InfoCallBack.FNewFileName := Trim(InfoCallBack.FNewFileName); case FileNotifyInfo^.Action of FILE_ACTION_RENAMED_OLD_NAME: FOldFileName := Trim(WideCharToString(@(FileNotifyInfo^.FileName[0]))); FILE_ACTION_RENAMED_NEW_NAME: InfoCallBack.FOldFileName := FOldFileName; end; FInfoCallBack(InfoCallBack); PChar(FileNotifyInfo) := PChar(FileNotifyInfo)+Offset; until (Offset=0) or Terminated; // End synchronize ZeroMemory(@FWatchBuf, SizeOf(FWatchBuf)); FBytesWritte := 0; ReadDirectoryChanges(FWatchHandle, @FWatchBuf, SizeOf(FWatchBuf), FSubTree, FFilter, @FBytesWritte, @FOverLapp, 0); end else Terminate; end end end; end.