【问题标题】:How to detect changes to files recursively?如何递归检测文件的更改?
【发布时间】:2013-03-24 19:37:29
【问题描述】:

我正在开发一个多线程组件来加载和管理音乐库,并且我有一个定义多个要包含的根目录的属性。一个线程在这些目录中搜索媒体文件,根据需要添加/删除,另一个线程遍历这些文件并填写 ID3v2 标记信息。我已经有了检测添加/删除文件的机制,但我不知道如何检测更改。

我如何检测其他外部应用程序何时对这些文件中的任何一个进行了更改?我想要一个即时响应,而不必等待线程访问该文件。当这些文件夹中的任何文件被递归更改时,我是否可以接收警报?

【问题讨论】:

    标签: delphi delphi-xe2 id3v2


    【解决方案1】:

    您需要使用的函数是ReadDirectoryChangesW。这不是世界上最容易使用的功能,值得指出的是它不是 100% 可靠的。它有时会无法通知您修改。根据我的经验,股票更有可能发生这种情况。

    此 API 可用于同步或异步模式。与往常一样,同步版本更容易编写代码。但当然它会阻塞调用线程。所以解决这个问题的方法是将ReadDirectoryChangesW 的调用放在不同的线程中。如果您有大量的目录要观看,那么每个目录一个观看线程将是一个不可行的负担。如果是这样,那么您将需要处理异步使用。

    bWatchSubtree 参数允许你监视整个目录树,我认为这是你想要做的。

    有关更多详细信息,请参阅这篇文章:Understanding ReadDirectoryChangesW

    【讨论】:

    • 谢谢,我可以为此创建另一个专用线程,并为其提供要监视的目录列表。
    • 如果您使用 API 的同步变体,则不会。在这种情况下,对 RDCW 的调用会阻塞。
    • 嗯,我确实有大量目录(超过 400 个),我打算让这个目录支持超过 5,000 个目录,所以我不能为每个目录创建一个线程。
    • @Jerry,为了实现你可以看看Runner写得很好的Directory Watch
    【解决方案2】:

    试试这个:

    uses
      ShlObj, ActiveX;
    
    const
      FILE_LIST_DIRECTORY   = $0001;
      cDir = 'E:\...'; // The directory to monitor
    
    Type
      PFileNotifyInformation = ^TFileNotifyInformation;
      TFileNotifyInformation = Record
        NextEntryOffset: DWORD;
        Action: DWORD;
        FileNameLength: DWORD;
        FileName: Array[0..0] of WideChar;
      End;
    
    type
      TWaitThread = class(TThread)
      private
        FForm: TMainForm;
        procedure HandleEvent;
      protected
        procedure Execute; override;
      public
        constructor Create(Form: TMainForm);
        Procedure SendFtp(F: String; AddIfError: Boolean);
      end;
    
    
    procedure TWaitThread.HandleEvent;
      Var
      FileOpNotification: PFileNotifyInformation;
      Offset: Longint;
      F: String;
      AList: TStringList;
      I: Integer;
    begin
    
      AList := TStringList.Create;
    
      Try
    
        With FForm Do
        Begin
    
          Pointer(FileOpNotification) := @FNotificationBuffer[0];
    
          Repeat
            Offset := FileOpNotification^.NextEntryOffset;
            //lbEvents.Items.Add(Format(SAction[FileOpNotification^.Action], [WideCharToString(@(FileOpNotification^.FileName))]));
    
            F := cDir + WideCharToString(@(FileOpNotification^.FileName));
    
            if AList.IndexOf(F) < 0 Then
            AList.Add(F);
    
            PChar(FileOpNotification) := PChar(FileOpNotification)+Offset;
    
          Until Offset=0;
    
          For I := 0 To AList.Count -1 Do
          // do whatever you need
    
        End;
    
      Finally
        AList.Free;
      End;
    
    end;
    
    
    constructor TWaitThread.Create(Form: TMainForm);
    begin
      inherited Create(True);
      FForm := Form;
      FreeOnTerminate := False;
    end;
    
    procedure TWaitThread.Execute;
      Var
      NumBytes: DWORD;
      CompletionKey: DWORD;
    begin
    
      While Not Terminated Do
      Begin
    
        GetQueuedCompletionStatus( FForm.FCompletionPort, numBytes, CompletionKey, FForm.FPOverlapped, INFINITE);
    
        if CompletionKey <> 0 Then
        Begin
          Synchronize(HandleEvent);
    
          With FForm do
          begin
            FBytesWritten := 0;
            ZeroMemory(@FNotificationBuffer, SizeOf(FNotificationBuffer));
            ReadDirectoryChanges(FDirectoryHandle, @FNotificationBuffer, SizeOf(FNotificationBuffer), False, FNotifyFilter, @FBytesWritten, @FOverlapped, nil);
          End;
    
        End
        Else
        Terminate;
    
      End;
    
    end;
    
    
    {MainForm}
    
      private
    
        FDirectoryHandle: THandle;
        FNotificationBuffer: array[0..4096] of Byte;
        FWatchThread: TThread;
        FNotifyFilter: DWORD;
        FOverlapped: TOverlapped;
        FPOverlapped: POverlapped;
        FBytesWritten: DWORD;
        FCompletionPort: THandle;
    
    
    procedure TMainForm.FormCreate(Sender: TObject);
    begin
    
      FCompletionPort := 0;
      FDirectoryHandle := 0;
      FPOverlapped := @FOverlapped;
      ZeroMemory(@FOverlapped, SizeOf(FOverlapped));
    
      Start;
    
    end;
    
    procedure TMainForm.Start;
    begin
    
      FNotifyFilter := 0;
    
      FNotifyFilter := FNotifyFilter or FILE_NOTIFY_CHANGE_FILE_NAME;
    
      FDirectoryHandle := CreateFile(cDir,
                          FILE_LIST_DIRECTORY,
                          FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
                          Nil,
                          OPEN_EXISTING,
                          FILE_FLAG_BACKUP_SEMANTICS or FILE_FLAG_OVERLAPPED,
                          0);
    
      if FDirectoryHandle = INVALID_HANDLE_VALUE Then
      Begin
        Beep;
        FDirectoryHandle := 0;
        ShowMessage(SysErrorMessage(GetLastError));
        Exit;
      End;
    
      FCompletionPort := CreateIoCompletionPort(FDirectoryHandle, 0, Longint(pointer(self)), 0);
      ZeroMemory(@FNotificationBuffer, SizeOf(FNotificationBuffer));
      FBytesWritten := 0;
    
      if Not ReadDirectoryChanges(FDirectoryHandle, @FNotificationBuffer, SizeOf(FNotificationBuffer), False, FNotifyFilter, @FBytesWritten, @FOverlapped, Nil) Then
      Begin
        CloseHandle(FDirectoryHandle);
        FDirectoryHandle := 0;
        CloseHandle(FCompletionPort);
        FCompletionPort := 0;
        ShowMessage(SysErrorMessage(GetLastError));
        Exit;
      End;
    
      FWatchThread := TWaitThread.Create(self);
      TWaitThread(FWatchThread).Resume;
    
    end;
    
    procedure TMainForm.Stop;
    begin
    
      if FCompletionPort = 0 Then
      Exit;
    
      PostQueuedCompletionStatus(FCompletionPort, 0, 0, Nil);
      FWatchThread.WaitFor;
      FWatchThread.Free;
      CloseHandle(FDirectoryHandle);
      FDirectoryHandle := 0;
      CloseHandle(FCompletionPort);
      FCompletionPort := 0;
    
    end;
    
    procedure TMainForm.FormDestroy(Sender: TObject);
    begin
      Stop;
    end;
    

    【讨论】:

    • 能否请您添加一些文字来描述此代码的作用以及它如何解决问题。
    • 如果它是一个现成的单元也很好,而不是一堆代码组合成一个大块
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-21
    • 1970-01-01
    • 2018-08-16
    相关资源
    最近更新 更多