发一个我写的组件.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.
|