windows – 为什么ReadDirectoryChangesW省略事件?

当检测到更改时,我使用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;

删除目录确实只返回一个更改,对其中包含的文件没有任何影响.但它确实有道理,因为你只是在看父母目录的句柄.如果您需要通知子目录,您可能还需要观看它们.

相关文章

Windows2012R2备用域控搭建 前置操作 域控主域控的主dns:自...
主域控角色迁移和夺取(转载) 转载自:http://yupeizhi.blo...
Windows2012R2 NTP时间同步 Windows2012R2里没有了internet时...
Windows注册表操作基础代码 Windows下对注册表进行操作使用的...
黑客常用WinAPI函数整理之前的博客写了很多关于Windows编程的...
一个简单的Windows Socket可复用框架说起网络编程,无非是建...