我正在研究一個多線程組件來加載和管理音樂庫,並且我有一個屬性定義了包含多個根目錄。一個線程搜索這些目錄中的媒體文件,根據需要添加/刪除,另一個線程遍歷這些文件並填充ID3v2標記信息。我已經有一個機制來檢測添加/刪除的文件,但我不知道如何檢測更改。如何遞歸檢測文件的更改?
如何檢測何時從其他外部應用程序對這些文件進行了更改?我想要一個即時響應,而不必等待一個線程才能到達該文件。有什麼辦法可以在任何文件夾中遞歸更改任何文件時收到警報?
我正在研究一個多線程組件來加載和管理音樂庫,並且我有一個屬性定義了包含多個根目錄。一個線程搜索這些目錄中的媒體文件,根據需要添加/刪除,另一個線程遍歷這些文件並填充ID3v2標記信息。我已經有一個機制來檢測添加/刪除的文件,但我不知道如何檢測更改。如何遞歸檢測文件的更改?
如何檢測何時從其他外部應用程序對這些文件進行了更改?我想要一個即時響應,而不必等待一個線程才能到達該文件。有什麼辦法可以在任何文件夾中遞歸更改任何文件時收到警報?
您需要使用的功能是ReadDirectoryChangesW
。這不是世界上最容易使用的功能,值得指出的是它不是100%可靠的。它有時會無法通知您修改。以我的經驗來看,股票更可能發生這種情況。
此API可用於同步或異步模式。與往常一樣,同步版本更容易編碼。但它當然會阻止調用線程。因此,出路的方法是將呼叫ReadDirectoryChangesW
置於不同的線程中。如果你有大量的目錄需要觀察,那麼每個目錄的一個觀察線程將是一個不可行的負擔。如果是這樣,那麼你需要解決異步使用問題。
您bWatchSubtree
參數允許你監視我認爲這是你想要做什麼目錄的整個樹。
欲瞭解更多詳情請您看看這篇文章:Understanding ReadDirectoryChangesW。
試試這個:
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-25 13:11:29
如果它是一個現成的單元,而不是一堆代碼組合成一個大塊 – 2013-03-25 13:22:34
謝謝,我可以讓另外一個專門的線程,這和餵養它的目錄監視列表。 – 2013-03-24 20:44:45
如果使用API的同步變體,則不適用。在這種情況下,調用RDCW塊。 – 2013-03-24 20:45:59
嗯,我有大量的目錄(超過400),我打算爲此支持超過5,000個目錄,所以我不能爲每個目錄創建一個線程。 – 2013-03-24 21:26:31