| 发一个我写的组件.FileSystemWatcher,目录文件监视. 
 
 可否加入CNPACK组件包?
 {*******************************************************}
 {                                                                                                }
 {       FileSystemWatcher                                                               }
 {                                                                                                }
 {       版权所有 (C) 2007 solokey                                                     }
 {                                                                                                }
 {       http://blog.csdn.net/solokey                                                 }
 {                                                                                                }
 {*******************************************************}
 
 
 unit FileSystemWatcher;
 
 interface
 uses
 Windows, Classes, SysUtils;
 
 type
 TFileOperation = (foAdded, foRemoved, foModified, foRenamed);
 TFileDealMethod = procedure(FileOperation: TFileOperation; const FileName1,FileName2: string) of object;
 
 TNotifyFilter = (nfFileNameChange, nfDirNameChange, nfAttributeChange,
 nfSizeChange, nfWriteChange, nfAccessChange, nfCreationDateChange, nfSecurityChange);
 TNotifyFilters = set of TNotifyFilter;
 
 TNotificationBuffer =  array[0..4095] of Byte;
 
 PFileNotifyInformation = ^TFileNotifyInformation;
 TFileNotifyInformation = record
 NextEntryOffset: DWORD;
 Action: DWORD;
 FileNameLength: DWORD;
 FileName: array[0..0] of WideChar;
 end;
 
 TShellChangeThread = class(TThread)
 private
 FActived: Boolean;
 FDirectoryHandle: Cardinal;
 FCS: TRTLCriticalSection;
 FChangeEvent: TFileDealMethod;
 FDirectory: string;
 FWatchSubTree: Boolean;
 FCompletionPort: Cardinal;
 FOverlapped: TOverlapped;
 FNotifyOptionFlags: DWORD;
 FBytesWritten: DWORD;
 FNotificationBuffer: TNotificationBuffer;
 protected
 procedure Execute; override;
 procedure DoIOCompletionEvent;
 function ResetReadDirctory: Boolean;
 procedure Lock;
 procedure Unlock;
 public
 constructor Create(ChangeEvent: TFileDealMethod); virtual;
 destructor Destroy; override;
 procedure SetDirectoryOptions(Directory : String; Actived: Boolean; WatchSubTree : Boolean;
 NotifyOptionFlags : DWORD);
 property ChangeEvent : TFileDealMethod read FChangeEvent write FChangeEvent;
 end;
 
 
 TFileSystemWatcher = class(TComponent)
 private
 FActived: Boolean;
 FWatchedDir: string;
 FThread: TShellChangeThread;
 FOnChange: TFileDealMethod;
 FWatchSubTree: Boolean;
 FFilters: TNotifyFilters;
 procedure SetWatchedDir(const Value: string);
 procedure SetWatchSubTree(const Value: Boolean);
 procedure SetOnChange(const Value: TFileDealMethod);
 procedure SetFilters(const Value: TNotifyFilters);
 function  NotifyOptionFlags: DWORD;
 procedure SetActived(const Value: Boolean);
 protected
 procedure Change;
 procedure Start;
 procedure Stop;
 public
 constructor Create(AOwner : TComponent); override;
 destructor  Destroy; override;
 published
 property  Actived:Boolean  read FActived write SetActived;
 property  WatchedDir: string read FWatchedDir write SetWatchedDir;
 property  WatchSubTree: Boolean read FWatchSubTree write SetWatchSubTree;
 property  NotifyFilters: TNotifyFilters read FFilters write SetFilters;
 property  OnChange: TFileDealMethod read FOnChange write SetOnChange;
 end;
 
 procedure  Register;
 
 implementation
 
 procedure  Register;
 begin
 RegisterComponents('Samples', [TFileSystemWatcher]);
 end;
 
 { TShellChangeThread }
 
 constructor TShellChangeThread.Create(ChangeEvent: TFileDealMethod);
 begin
 FreeOnTerminate := True;
 FChangeEvent := ChangeEvent;
 InitializeCriticalSection(FCS);
 FDirectoryHandle := 0;
 FCompletionPort := 0;
 inherited Create(True);
 end;
 
 destructor TShellChangeThread.Destroy;
 begin
 CloseHandle(FDirectoryHandle);
 CloseHandle(FCompletionPort);
 DeleteCriticalSection(FCS);
 inherited Destroy;
 end;
 
 procedure TShellChangeThread.DoIOCompletionEvent;
 var
 TempBuffer: TNotificationBuffer;
 FileOpNotification: PFileNotifyInformation;
 Offset: Longint;
 FileName1, FileName2: string;
 FileOperation: TFileOperation;
 procedure DoDirChangeEvent;
 begin
 if Assigned(ChangeEvent) and FActived then
 ChangeEvent(FileOperation, FileName1, FileName2);
 end;
 function  CompleteFileName(const FileName:string):string;
 begin
 Result := '';
 if Trim(FileName) <> '' then
 Result := FDirectory + FileName;
 end;
 begin
 Lock;
 TempBuffer := FNotificationBuffer;
 FillChar(FNotificationBuffer, SizeOf(FNotificationBuffer), 0);
 Unlock;
 Pointer(FileOpNotification) := @TempBuffer[0];
 repeat
 with FileOpNotification^ do begin
 Offset := NextEntryOffset;
 FileName2 := '';
 case Action of
 FILE_ACTION_ADDED..FILE_ACTION_MODIFIED: begin
 FileName1 := CompleteFileName(WideCharLenToString(@FileName, FileNameLength div SizeOf(WideChar)));
 FileOperation := TFileOperation(Action - 1);
 DoDirChangeEvent;
 end;
 FILE_ACTION_RENAMED_OLD_NAME: begin
 FileName1 := CompleteFileName(WideCharLenToString(@FileName, FileNameLength div SizeOf(WideChar)));
 FileOperation := TFileOperation(Action - 1);
 end;
 FILE_ACTION_RENAMED_NEW_NAME: begin
 if FileOperation = foRenamed then begin
 FileName2 := CompleteFileName(WideCharLenToString(@FileName, FileNameLength div SizeOf(WideChar)));
 DoDirChangeEvent;
 end;
 end;
 end;
 end;
 Pointer(FileOpNotification) := Pointer(PChar(FileOpNotification) + OffSet);
 until Offset=0;
 end;
 
 procedure TShellChangeThread.Execute;
 var
 numBytes: DWORD;
 CompletionKey: DWORD;
 PFOverlapped: POverlapped;
 TempDirectoryHandle: Cardinal;
 TempCompletionPort: Cardinal;
 begin
 while not Terminated do begin
 Lock;
 TempDirectoryHandle := FDirectoryHandle;
 TempCompletionPort := FCompletionPort;
 Unlock;
 if TempDirectoryHandle > 0  then begin
 PFOverlapped := @FOverlapped;
 GetQueuedCompletionStatus(TempCompletionPort, numBytes, CompletionKey, PFOverlapped, INFINITE);
 if CompletionKey = Handle then begin
 Synchronize(DoIOCompletionEvent);
 FBytesWritten := 0;
 FillChar(FNotificationBuffer, SizeOf(FNotificationBuffer), 0);
 ReadDirectoryChanges(FDirectoryHandle, @FNotificationBuffer, SizeOf(FNotificationBuffer), FWatchSubTree, FNotifyOptionFlags, @FBytesWritten, @FOverlapped, nil);
 end;
 end;
 end;
 PostQueuedCompletionStatus(TempCompletionPort, numBytes, CompletionKey, PFOverlapped);
 end;
 
 procedure TShellChangeThread.Lock;
 begin
 EnterCriticalSection(FCS);
 end;
 
 function TShellChangeThread.ResetReadDirctory: Boolean;
 var
 TempHandle: Cardinal;
 TempCompletionPort: Cardinal;
 begin
 Result := False;
 CloseHandle(FDirectoryHandle);
 PostQueuedCompletionStatus(FCompletionPort, 0, 0, nil);
 CloseHandle(FCompletionPort);
 
 
 TempHandle := CreateFile(PChar(FDirectory), GENERIC_READ or GENERIC_WRITE,
 FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
 nil, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS or FILE_FLAG_OVERLAPPED, 0);
 Lock;
 FDirectoryHandle := TempHandle;
 Unlock;
 
 if (GetLastError = ERROR_FILE_NOT_FOUND) or (GetLastError = ERROR_PATH_NOT_FOUND) then begin
 Lock;
 FDirectoryHandle := 0;
 FCompletionPort := 0;
 Unlock;
 Exit;
 end;
 
 TempCompletionPort := CreateIoCompletionPort(FDirectoryHandle, 0, Handle, 0);
 
 Lock;
 FCompletionPort := TempCompletionPort;
 Unlock;
 
 FBytesWritten := 0;
 FillChar(FNotificationBuffer, SizeOf(FNotificationBuffer), 0);
 Result := ReadDirectoryChanges(FDirectoryHandle, @FNotificationBuffer, SizeOf(FNotificationBuffer), FWatchSubTree, FNotifyOptionFlags, @FBytesWritten, @FOverlapped, nil);
 end;
 
 procedure TShellChangeThread.SetDirectoryOptions(Directory: String; Actived: Boolean;
 WatchSubTree: Boolean;  NotifyOptionFlags : DWORD);
 begin
 FWatchSubTree := WatchSubTree;
 FNotifyOptionFlags := NotifyOptionFlags;
 FDirectory := IncludeTrailingBackslash(Directory);
 FActived := Actived;
 ResetReadDirctory;
 end;
 
 procedure TShellChangeThread.Unlock;
 begin
 LeaveCriticalSection(FCS);
 end;
 
 { TFileSystemWatcher }
 
 procedure TFileSystemWatcher.Change;
 begin
 if csDesigning in ComponentState then
 Exit;
 if Assigned(FThread) then begin
 FThread.SetDirectoryOptions(FWatchedDir, FActived, LongBool(FWatchSubTree), NotifyOptionFlags);
 end;
 end;
 
 constructor TFileSystemWatcher.Create(AOwner: TComponent);
 begin
 inherited Create(AOwner);
 FActived := False;
 FWatchedDir := 'C:\';
 FFilters := [nfFilenameChange, nfDirNameChange];
 FWatchSubTree := True;
 FOnChange := nil;
 end;
 
 destructor TFileSystemWatcher.Destroy;
 begin
 if Assigned(FThread) then
 FThread.Terminate;
 inherited Destroy;
 end;
 
 function TFileSystemWatcher.NotifyOptionFlags: DWORD;
 begin
 Result := 0;
 if nfFileNameChange in FFilters then
 Result := Result or FILE_NOTIFY_CHANGE_FILE_NAME;
 if nfDirNameChange in FFilters then
 Result := Result or FILE_NOTIFY_CHANGE_DIR_NAME;
 if nfSizeChange in FFilters then
 Result := Result or FILE_NOTIFY_CHANGE_SIZE;
 if nfAttributeChange in FFilters then
 Result := Result or FILE_NOTIFY_CHANGE_ATTRIBUTES;
 if nfWriteChange in FFilters then
 Result := Result or FILE_NOTIFY_CHANGE_LAST_WRITE;
 if nfAccessChange in FFilters then
 Result := Result or FILE_NOTIFY_CHANGE_LAST_ACCESS;
 if nfCreationDateChange in FFilters then
 Result := Result or FILE_NOTIFY_CHANGE_CREATION;
 if nfSecurityChange in FFilters then
 Result := Result or FILE_NOTIFY_CHANGE_SECURITY;
 end;
 
 procedure TFileSystemWatcher.SetActived(const Value: Boolean);
 begin
 if FActived <> Value then begin
 FActived := Value;
 Change;
 if FActived then
 Start
 else
 Stop;
 end;
 end;
 
 procedure TFileSystemWatcher.SetFilters(const Value: TNotifyFilters);
 begin
 if FFilters <> Value then begin
 FFilters := Value;
 Change;
 end;
 end;
 
 procedure TFileSystemWatcher.SetOnChange(const Value: TFileDealMethod);
 begin
 FOnChange := Value;
 if Assigned(FOnChange) and FActived then
 Start
 else
 Stop;
 Change;
 end;
 
 procedure TFileSystemWatcher.SetWatchedDir(const Value: string);
 begin
 if not SameText(FWatchedDir, Value) then begin
 FWatchedDir := Value;
 Change;
 end;
 end;
 
 procedure TFileSystemWatcher.SetWatchSubTree(const Value: Boolean);
 begin
 if FWatchSubTree <> Value then begin
 FWatchSubTree := Value;
 Change;
 end;
 end;
 
 procedure TFileSystemWatcher.Start;
 begin
 if csDesigning in ComponentState then
 Exit;
 if Assigned(FOnChange) then begin
 FThread := TShellChangeThread.Create(FOnChange);
 FThread.SetDirectoryOptions(FWatchedDir, FActived, LongBool(FWatchSubTree), NotifyOptionFlags);
 FThread.Resume;
 end;
 end;
 
 procedure TFileSystemWatcher.Stop;
 begin
 if csDesigning in ComponentState then
 Exit;
 if Assigned(FThread) then begin
 FThread.Terminate;
 FThread := nil;
 end;
 end;
 
 end.
 |