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;@H_404_2@ 
 

过滤器是

FFilter :=  FILE_NOTIFY_CHANGE_FILE_NAME or
            FILE_NOTIFY_CHANGE_DIR_NAME or
            FILE_NOTIFY_CHANGE_SIZE or
            FILE_NOTIFY_CHANGE_LAST_WRITE;@H_404_2@ 
 

目录句柄是这样获得的:

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);@H_404_2@ 
 

当我删除多个文件时,我只得到一个事件,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;@H_404_2@ 
 

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

相关文章

(1)when you ping a computer from itsafe,the ping command should return the local IP address. (...
1、点击win菜单,点击设置图标 2、选择系统选项 3、选择应用与程序选项 4、拉到最下方,选择程序与功能...
目前一直直接往Windows 2008 R2 Server中复制文件(暂时还没有搭建ftp服务),突然不能复制了,于是百度...
windows下使用vscode配合xebug调试php脚本 要下载有php_xebug.dll扩展的版本,最新版可能没有这个扩展,p...
在控制面板的程序与功能里启用和关闭windows功能打开,适用于linux的windows子系统
效果演示 推荐一个非常牛的文档网站生成器:docsify 我通过这个工具,成功将码云上的个人学习笔记发布到...