当检测到更改时,我使用ReadDirectoryChangesW来监视指定的目录并更新索引结构.我使用以下代码(大致)
var InfoPointer : PFileNotifyinformation; NextOffset : DWORD; ... while (not Terminated) do begin if ReadDirectoryChangesW (FDirHandle,FBuffer,FBufferLength,True,FFilter,@BytesRead,@FOverlap,nil) then begin WaitResult := WaitForMultipleObjects (2,@FEventArray,False,INFINITE); if (WaitResult = waitFileChange) then begin InfoPointer := FBuffer; repeat NextOffset := InfoPointer.NextEntryOffset; ... PByte (InfoPointer) := PByte (InfoPointer) + NextOffset; until NextOffset = 0; end; end; end;
过滤器是
FFilter := FILE_NOTIFY_CHANGE_FILE_NAME or FILE_NOTIFY_CHANGE_DIR_NAME or FILE_NOTIFY_CHANGE_SIZE or FILE_NOTIFY_CHANGE_LAST_WRITE;
目录句柄是这样获得的:
FDirHandle := CreateFile (PChar (FDirectoryWatch.WatchedDirectory),FILE_LIST_DIRECTORY or GENERIC_READ,FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,nil,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS or FILE_FLAG_OVERLAPPED,0);
当我删除多个文件时,我只得到一个事件,NextOffset为0!当我删除一个目录时,我只得到一个目录的事件.如果我想为目录中的每个文件一个事件怎么办?
任何帮助将不胜感激.
在我看来,您正在混合使用ReadDirectoryChangesW()的各种方法,当打开目录并指定lpOverlapped参数时,您都要指定FILE_FLAG_OVERLAPPED标志,这意味着要等待结构中的事件和句柄异步I / O;同时在工作线程的循环中调用ReadDirectoryChangesW().我会先尝试使用lpOverlapped设置为nil,因为你有一个专用的线程,可以使用同步模式.
在ReadDirectoryChangesW() API函数的文档中描述了不同的使用方法.请注意,缓冲区溢出也是可能的,所以改变事件也可能会丢失.也许你应该重新思考你完全依靠这个功能的策略,比较目录内容的快照也可以工作.
编辑:
您编辑的代码看起来更好.然而,在我的测试中,ReadDirectoryChangesW()的功能是按照通告方式工作的,返回的缓冲区中有几个数据条目,或者有多个缓冲区要处理.这取决于时间,在Delphi中击中断点后,我在一个缓冲区中获取了几个条目.
为了完整,我附上测试代码,使用Delphi 5实现:
type TWatcherThread = class(TThread) private fChangeHandle: THandle; fDirHandle: THandle; fShutdownHandle: THandle; protected procedure Execute; override; public constructor Create(ADirectoryToWatch: string); destructor Destroy; override; procedure Shutdown; end; constructor TWatcherThread.Create(ADirectoryToWatch: string); const FILE_LIST_DIRECTORY = 1; begin inherited Create(TRUE); fChangeHandle := CreateEvent(nil,FALSE,nil); fDirHandle := CreateFile(PChar(ADirectoryToWatch),FILE_FLAG_BACKUP_SEMANTICS or FILE_FLAG_OVERLAPPED,0); fShutdownHandle := CreateEvent(nil,nil); Resume; end; destructor TWatcherThread.Destroy; begin if fDirHandle <> INVALID_HANDLE_VALUE then CloseHandle(fDirHandle); if fChangeHandle <> 0 then CloseHandle(fChangeHandle); if fShutdownHandle <> 0 then CloseHandle(fShutdownHandle); inherited Destroy; end; procedure TWatcherThread.Execute; type PFileNotifyinformation = ^TFileNotifyinformation; TFileNotifyinformation = record NextEntryOffset: DWORD; Action: DWORD; FileNameLength: DWORD; FileName: WideChar; end; const BufferLength = 65536; var Filter,BytesRead: DWORD; InfoPointer: PFileNotifyinformation; Offset,NextOffset: DWORD; Buffer: array[0..BufferLength - 1] of byte; Overlap: TOverlapped; Events: array[0..1] of THandle; WaitResult: DWORD; FileName,s: string; begin if fDirHandle <> INVALID_HANDLE_VALUE then begin Filter := FILE_NOTIFY_CHANGE_FILE_NAME or FILE_NOTIFY_CHANGE_DIR_NAME or FILE_NOTIFY_CHANGE_SIZE or FILE_NOTIFY_CHANGE_LAST_WRITE; FillChar(Overlap,SizeOf(TOverlapped),0); Overlap.hEvent := fChangeHandle; Events[0] := fChangeHandle; Events[1] := fShutdownHandle; while not Terminated do begin if ReadDirectoryChangesW (fDirHandle,@Buffer[0],BufferLength,TRUE,Filter,@Overlap,nil) then begin WaitResult := WaitForMultipleObjects(2,@Events[0],INFINITE); if WaitResult = WAIT_OBJECT_0 then begin InfoPointer := @Buffer[0]; Offset := 0; repeat NextOffset := InfoPointer.NextEntryOffset; FileName := WideCharLenToString(@InfoPointer.FileName,InfoPointer.FileNameLength); SetLength(FileName,StrLen(PChar(FileName))); s := Format('[%d] Action: %.8xh,File: "%s"',[Offset,InfoPointer.Action,FileName]); OutputDebugString(PChar(s)); PByte(InfoPointer) := PByte(DWORD(InfoPointer) + NextOffset); Offset := Offset + NextOffset; until NextOffset = 0; end; end; end; end; end; procedure TWatcherThread.Shutdown; begin Terminate; if fShutdownHandle <> 0 then SetEvent(fShutdownHandle); end; //////////////////////////////////////////////////////////////////////////////// procedure TForm1.FormCreate(Sender: TObject); begin fThread := TWatcherThread.Create('D:\Temp'); end; procedure TForm1.FormDestroy(Sender: TObject); begin if fThread <> nil then begin TWatcherThread(fThread).Shutdown; fThread.Free; end; end;
删除目录确实只返回一个更改,对其中包含的文件没有任何影响.但它确实有道理,因为你只是在看父母目录的句柄.如果您需要通知子目录,您可能还需要观看它们.