123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- unit DirectoryMonitor;
- // Based on DirMon by phaeteon and LoLa
- interface
- uses
- 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;
- implementation
- type
- // 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.
|