1

私は音楽ライブラリをロードして管理するためのマルチスレッドコンポーネントに取り組んでおり、含める複数のルートディレクトリを定義するプロパティがあります。1つのスレッドがそれらのディレクトリでメディアファイルを検索し、必要に応じて追加/削除し、別のスレッドがそれらのファイルを調べてID3v2タグ情報を入力します。追加/削除されたファイルを検出するメカニズムはすでにありますが、変更を検出する方法がわかりません。

他の外部アプリケーションからこれらのファイルに変更が加えられたことをどのように検出できますか?瞬時の応答が欲しいのですが、スレッドがそのファイルに到達するのを待つ必要はありません。これらのフォルダのいずれかでファイルが再帰的に変更されたときにアラートを受信する方法はありますか?

4

2 に答える 2

3

使用する必要のある関数はですReadDirectoryChangesW。これは世界で最も使いやすい関数ではなく、100%信頼できるわけではないことを指摘する価値があります。変更の通知に失敗する場合があります。私の経験では、それは株式で発生する可能性が高いです。

このAPIは、同期モードまたは非同期モードで使用できます。いつものように、同期バージョンはコーディングがはるかに簡単です。しかしもちろん、それは呼び出し元のスレッドをブロックします。したがって、それを回避する方法は、呼び出しをReadDirectoryChangesW別のスレッドに配置することです。監視するディレクトリの数が非常に多い場合、ディレクトリごとに1つの監視スレッドが実行不可能な負担になります。その場合は、非同期の使用法に取り組む必要があります。

YoubWatchSubtreeパラメータを使用すると、ディレクトリのツリー全体を監視できます。これは、あなたがやりたいことだと思います。

詳細については、この記事「ReadDirectoryChangesWについて」を参照してください。

于 2013-03-24T20:07:14.240 に答える
0

これを試して:

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;
于 2013-03-25T10:58:18.420 に答える