Delphi 如何递归地检测对文件的更改?

Delphi 如何递归地检测对文件的更改?,delphi,delphi-xe2,id3v2,Delphi,Delphi Xe2,Id3v2,我正在开发一个多线程组件来加载和管理音乐库,我有一个属性定义了要包含的多个根目录。一个线程搜索这些目录中的媒体文件,根据需要添加/删除,另一个线程遍历这些文件并填写ID3v2标记信息。我已经有了检测添加/删除文件的机制,但我不知道如何检测更改 如何检测其他外部应用程序何时对这些文件进行了更改?我想要即时响应,而不必等待线程访问该文件。当这些文件夹中的任何文件被递归更改时,是否有一种方法可以接收警报?您需要使用的功能是。这不是世界上最容易使用的功能,值得指出的是,它不是100%可靠的。它有时无法通

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


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

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

此API可以在同步或异步模式下使用。与往常一样,同步版本更容易编码。但它当然会阻止调用线程。因此,解决方法是在不同的线程中调用
ReadDirectoryChangesW
。如果要监视大量目录,那么每个目录一个监视线程将是一个不可行的负担。如果是这样的话,那么您需要解决异步使用问题

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

有关更多详细信息,请参阅本文:

试试这个:

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;
使用
ShlObj,ActiveX;
常数
文件列表目录=$0001;
cDir='E:\…';//要监视的目录
类型
PFileNotifyInformation=^TFileNotifyInformation;
TFileNotifyInformation=记录
下一个偏移量:DWORD;
行动:德沃德;
文件名长度:DWORD;
文件名:WideChar的数组[0..0];
结束;
类型
TWaitThread=class(TThread)
私有的
FForm:tma通知;
程序句柄;
受保护的
程序执行;推翻
公众的
建造商创建(表格:TMainForm);
过程SendFtp(F:String;AddIfError:Boolean);
结束;
程序TWaitThread.HandleEvent;
变量
FileOpNotification:PFileNotifyInformation;
偏移量:长;
F:字符串;
作者:TStringList;
I:整数;
开始
AList:=TStringList.Create;
尝试
带着我做的
开始
指针(FileOpNotification):=@FNotificationBuffer[0];
重复
偏移量:=FileOpNotification^.NextEntryOffset;
//添加(格式(SAction[FileOpNotification^.Action],[WideCharToString(@(FileOpNotification^.FileName)));
F:=cDir+WideCharToString(@(FileOpNotification^.FileName));
如果列表索引F(F)<0,则
3.添加(F);
PChar(FileOpNotification):=PChar(FileOpNotification)+偏移量;
直到偏移量=0;
对于I:=0到AList.Count-1 Do
//你需要什么就做什么
结束;
最后
免费的;
结束;
结束;
构造函数TWaitThread.Create(形式:TMainForm);
开始
继承创建(True);
FForm:=形式;
FreeOnTerminate:=False;
结束;
过程TWaitThread.Execute;
变量
NumBytes:德沃德;
完成键:DWORD;
开始
虽然没有终止
开始
GetQueuedCompletionStatus(FForm.fcCompletionPort,numBytes,CompletionKey,FForm.FPOverlapped,无限);
如果CompletionKey为0,则
开始
同步(HandleEvent);
带着我做的
开始
fBytesWrited:=0;
零内存(@FNotificationBuffer,SizeOf(FNotificationBuffer));
ReadDirectoryChanges(FDirectoryHandle、@FNotificationBuffer、SizeOf(FNotificationBuffer)、False、FNotifyFilter、@fBytesWrite、@FOverlapped、nil);
结束;
终点
其他的
终止
结束;
结束;
{MainForm}
私有的
董事会主席:唐德尔;
FNotificationBuffer:字节的数组[0..4096];
FWatchThread:TThread;
FNotifyFilter:DWORD;
FOverlapped:TOverlapped;
FP重叠:POverlapped;
fBytes:德沃德;
F完成端口:坦德尔;
程序TMAInformCreate(发送方:ToObject);
开始
FCompletionPort:=0;
FDirectoryHandle:=0;
FPOverlapped:=@FOverlapped;
零内存(@FOverlapped,SizeOf(FOverlapped));
开始
结束;
程序启动;
开始
FNotifyFilter:=0;
FNotifyFilter:=FNotifyFilter或FILE\u NOTIFY\u CHANGE\u FILE\u NAME;
FDirectoryHandle:=CreateFile(cDir,
文件列表目录,
文件共享读取或文件共享写入或文件共享删除,
无
开放式,
文件\u标志\u备份\u语义或文件\u标志\u重叠,
0);
如果FDirectoryHandle=无效的句柄值,则
开始
嘟嘟声
FDirectoryHandle:=0;
ShowMessage(SysErrorMessage(GetLastError));
出口
结束;
fcCompletionPort:=CreateIoCompletionPort(FDirectoryHandle,0,Longint(指针(自身)),0);
零内存(@FNotificationBuffer,SizeOf(FNotificationBuffer));
fBytesWrited:=0;
如果没有ReadDirectoryChanges(FDirectoryHandle、@FNotificationBuffer、SizeOf(FNotificationBuffer)、False、FNotifyFilter、@fBytesWrite、@FOverlapped、Nil),则
开始
CloseHandle(FDirectoryHandle);
FDirectoryHandle:=0;
关闭手柄(FCompletionPort);
FCompletionPort:=0;
ShowMessage(SysErrorMessage(GetLastError));
出口
结束;
FWatchThread:=TWaitThread.Create(self);
TWaitThread(FWatchThread).继续;
结束;
程序。停止;
开始
如果FCompletionPort=0,则
出口
PostQueuedCompletionStatus(FCompletionPort,0,0,Nil);
FWatchThread.WaitFor;
免费的;
CloseHandle(FDirectoryHandle);
FDirectoryHandle:=0;
关闭手柄(FCompletionPort);
FCompletionPort:=0;
结束;
程序TMAInformDestroy(发送方:TObject);
开始
停止
结束;
谢谢,我可以做