2013-03-24 25 views
1

我正在研究一個多線程組件來加載和管理音樂庫,並且我有一個屬性定義了包含多個根目錄。一個線程搜索這些目錄中的媒體文件,根據需要添加/刪除,另一個線程遍歷這些文件並填充ID3v2標記信息。我已經有一個機制來檢測添加/刪除的文件,但我不知道如何檢測更改。如何遞歸檢測文件的更改?

如何檢測何時從其他外部應用程序對這些文件進行了更改?我想要一個即時響應,而不必等待一個線程才能到達該文件。有什麼辦法可以在任何文件夾中遞歸更改任何文件時收到警報?

回答

3

您需要使用的功能是ReadDirectoryChangesW。這不是世界上最容易使用的功能,值得指出的是它不是100%可靠的。它有時會無法通知您修改。以我的經驗來看,股票更可能發生這種情況。

此API可用於同步或異步模式。與往常一樣,同步版本更容易編碼。但它當然會阻止調用線程。因此,出路的方法是將呼叫ReadDirectoryChangesW置於不同的線程中。如果你有大量的目錄需要觀察,那麼每個目錄的一個觀察線程將是一個不可行的負擔。如果是這樣,那麼你需要解決異步使用問題。

bWatchSubtree參數允許你監視我認爲這是你想要做什麼目錄的整個樹。

欲瞭解更多詳情請您看看這篇文章:Understanding ReadDirectoryChangesW

+0

謝謝,我可以讓另外一個專門的線程,這和餵養它的目錄監視列表。 – 2013-03-24 20:44:45

+0

如果使用API​​的同步變體,則不適用。在這種情況下,調用RDCW塊。 – 2013-03-24 20:45:59

+0

嗯,我有大量的目錄(超過400),我打算爲此支持超過5,000個目錄,所以我不能爲每個目錄創建一個線程。 – 2013-03-24 21:26:31

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; 
+1

您可以添加一些文字來描述此代碼的功能以及它如何解決問題。 – 2013-03-25 13:11:29

+0

如果它是一個現成的單元,而不是一堆代碼組合成一個大塊 – 2013-03-25 13:22:34