| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 | unit DirectoryMonitor;// Based on DirMon by phaeteon and LoLainterfaceuses  Windows, Messages, SysUtils, Classes, CompThread;type  EDirectoryMonitorError = class(Exception);  TFileChangedEvent = procedure(Sender: TObject; const FileName: string) of Object;  TFileRenamedEvent = procedure(Sender: TObject; const FromFileName: string; const ToFileName: string) of Object;  TDirectoryMonitor = class(TComponent)  private    FDirectoryHandle: THandle;    FNotificationBuffer: array[0..4096] of Byte;    FWatchThread: TCompThread;    FWatchFilters: DWord;    FOverlapped: TOverlapped;    FPOverlapped: POverlapped;    FBytesWritten: DWORD;    FCompletionPort: THandle;    FPath: string;    FActive: Boolean;    FOnCreated: TFileChangedEvent;    FOnDeleted: TFileChangedEvent;    FOnModified: TFileChangedEvent;    FOnRenamed: TFileRenamedEvent;    FWatchSubTree: Boolean;    procedure SetActive(AActive: Boolean);    procedure SetPath(aPath: string);  protected    procedure Start;    procedure Stop;    procedure DoCreated(Sender: TObject; FileName: string);    procedure DoDeleted(Sender: TObject; FileName: string);    procedure DoModified(Sender: TObject; FileName: string);    procedure DoRenamed(Sender: TObject; FromFileName: string; ToFileName: string);  public    constructor Create(AOwner: TComponent); override;    destructor Destroy; override;  published    property Active: Boolean read FActive write SetActive;    property Path: string read FPath write SetPath;    property OnCreated: TFileChangedEvent read FOnCreated write FOnCreated;    property OnDeleted: TFileChangedEvent read FOnDeleted write FOnDeleted;    property OnModified: TFileChangedEvent read FOnModified write FOnModified;    property OnRenamed: TFileRenamedEvent read FOnRenamed write FOnRenamed;    property WatchSubtree: Boolean read FWatchSubTree write FWatchSubtree;    property WatchFilters: DWord read FWatchFilters write FWatchFilters;  end;implementationtype  // See Windows API help  PFileNotifyInformation = ^TFileNotifyInformation;  TFileNotifyInformation = record    NextEntryOffset: DWord;    Action: DWord;    FileNameLength: DWord;    FileName: array[0..0] of WideChar;  end;const  FILE_LIST_DIRECTORY = $0001;type  TDirectoryMonitorThread = class(TCompThread)  private    FParent: TDirectoryMonitor;    FRenamedFrom: string;    procedure HandleEvent;  protected    procedure Execute; override;  public    constructor Create(AParent: TDirectoryMonitor);  end;constructor TDirectoryMonitorThread.Create(AParent: TDirectoryMonitor);begin  inherited Create(True);  FreeOnTerminate := False;  FParent := AParent;end;procedure TDirectoryMonitorThread.HandleEvent;var  FileOpNotification: PFileNotifyInformation;  Offset: DWord;  FileName: string;begin  FileOpNotification := PFileNotifyInformation(@FParent.FNotificationBuffer[0]);  repeat    Offset := FileOpNotification^.NextEntryOffset;    FileName := WideCharLenToString(@(FileOpNotification^.FileName), FileOpNotification^.FileNameLength div SizeOf(Char));    case FileOpNotification^.Action of      1: FParent.DoCreated(FParent, FileName);      2: FParent.DoDeleted(FParent, FileName);      3: FParent.DoModified(FParent, FileName);      4: FRenamedFrom := FileName;      5: FParent.DoRenamed(FParent, FRenamedFrom, FileName);    end;    FileOpNotification := PFileNotifyInformation(IntPtr(FileOpNotification) + IntPtr(Offset));  until (Offset = 0);end;procedure TDirectoryMonitorThread.Execute;var  NumBytes: DWord;  CompletionKey: ULONG_PTR;begin  while (not Terminated) do  begin    GetQueuedCompletionStatus(FParent.FCompletionPort, NumBytes, CompletionKey, FParent.FPOverlapped, INFINITE);    if CompletionKey <> 0 then    begin      Synchronize(HandleEvent);      with FParent do      begin        FBytesWritten := 0;        ZeroMemory(@FNotificationBuffer, SizeOf(FNotificationBuffer));        ReadDirectoryChanges(FDirectoryHandle, @FNotificationBuffer, SizeOf(FNotificationBuffer), FParent.WatchSubtree, WatchFilters, @FBytesWritten, @FOverlapped, nil);      end;    end      else    begin      Terminate;    end;  end;end;{ TDirectoryMonitor }constructor TDirectoryMonitor.Create(AOwner: TComponent);begin  inherited;  FCompletionPort := 0;  FDirectoryHandle := 0;  FPOverlapped := @FOverlapped;  ZeroMemory(@FOverlapped, SizeOf(FOverlapped));  FWatchFilters := FILE_NOTIFY_CHANGE_FILE_NAME or FILE_NOTIFY_CHANGE_DIR_NAME or FILE_NOTIFY_CHANGE_LAST_WRITE or FILE_NOTIFY_CHANGE_CREATION;end;destructor TDirectoryMonitor.Destroy;begin  if FActive then Stop;  inherited;end;procedure TDirectoryMonitor.SetActive(AActive: Boolean);begin  if csDesigning in ComponentState then Exit;  if AActive <> FActive then  begin    if AActive then Start      else Stop;  end;end;procedure TDirectoryMonitor.Start;begin  FDirectoryHandle :=    CreateFile(      PChar(FPath),      FILE_LIST_DIRECTORY,      FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,      nil,      OPEN_EXISTING,      FILE_FLAG_BACKUP_SEMANTICS or FILE_FLAG_OVERLAPPED,      0);  if FDirectoryHandle = INVALID_HANDLE_VALUE then  begin    FDirectoryHandle := 0;    raise EDirectoryMonitorError.Create(SysErrorMessage(GetLastError));    exit;  end;  FCompletionPort := CreateIoCompletionPort(FDirectoryHandle, 0, LongInt(Pointer(Self)), 0);  ZeroMemory(@FNotificationBuffer, SizeOf(FNotificationBuffer));  FBytesWritten := 0;  if not ReadDirectoryChanges(FDirectoryHandle, @FNotificationBuffer, SizeOf(FNotificationBuffer), FWatchSubTree, WatchFilters, @FBytesWritten, @FOverlapped, nil) then  begin    CloseHandle(FDirectoryHandle);    FDirectoryHandle := 0;    CloseHandle(FCompletionPort);    FCompletionPort := 0;    raise EDirectoryMonitorError.Create(SysErrorMessage(GetLastError));    exit;  end;  FWatchThread := TDirectoryMonitorThread.Create(Self);  TDirectoryMonitorThread(FWatchThread).Resume;  FActive := True;end;procedure TDirectoryMonitor.Stop;begin  PostQueuedCompletionStatus(FCompletionPort, 0, 0, nil);  FWatchThread.WaitFor;  FWatchThread.Free;  CloseHandle(FDirectoryHandle);  FDirectoryHandle := 0;  CloseHandle(FCompletionPort);  FCompletionPort := 0;  FActive := False;end;procedure TDirectoryMonitor.DoCreated(Sender: TObject; FileName: string);begin  if Assigned(FOnCreated) then FOnCreated(Sender, FPath + FileName);end;procedure TDirectoryMonitor.DoDeleted(Sender: TObject; FileName: string);begin  if Assigned(FOnDeleted) then FOnDeleted(Sender, FPath + FileName);end;procedure TDirectoryMonitor.DoModified(Sender: TObject; FileName: string);begin  if Assigned(FOnModified) then FOnModified(Sender, FPath + FileName);end;procedure TDirectoryMonitor.DoRenamed(Sender: TObject; FromFileName: string; ToFileName: string);begin  if Assigned(FOnRenamed) then FOnRenamed(Sender, FPath + FromFileName, FPath + ToFileName);end;procedure TDirectoryMonitor.SetPath(APath: string);begin  APath := IncludeTrailingPathDelimiter(APath);  if APath <> FPath then  begin    FPath := APath;    if FActive then    begin      Stop;      Start;    end;  end;end;end.
 |