Dường như với tôi rằng bạn đang trộn các cách khác nhau để sử dụng ReadDirectoryChangesW(), bạn cả hai xác định CỜ FILE _ _ chồng chéo cờ khi mở thư mục và cung cấp một con trỏ đến lpOverlapped tham số , nghĩa là bạn muốn đợi sự kiện trong cấu trúc và xử lý I/O không đồng bộ; và cùng lúc bạn gọi ReadDirectoryChangesW() trong một vòng lặp trong chuỗi công nhân. Trước tiên, tôi sẽ thử lại với lpOverlapped được đặt thành nil, vì bạn có một chuỗi chuyên dụng và có thể sử dụng chế độ đồng bộ.
Trong tài liệu của hàm ReadDirectoryChangesW() API, các cách khác nhau để sử dụng nó được mô tả. Lưu ý rằng nó cũng có thể là tràn bộ đệm, do đó, thay đổi các sự kiện có thể bị mất anyway. Có lẽ bạn nên suy nghĩ lại chiến lược của bạn chỉ dựa vào chức năng này, so sánh ảnh chụp nhanh của nội dung thư mục cũng có thể hoạt động.
Edit:
đang thay đổi nội dung của bạn trông đẹp hơn. Trong các thử nghiệm của tôi, tuy nhiên ReadDirectoryChangesW() đã hoạt động như được quảng cáo, có một vài mục nhập dữ liệu trong bộ đệm được trả về hoặc có nhiều bộ đệm để xử lý. Điều này phụ thuộc vào thời gian, sau khi nhấn một breakpoint trong Delphi tôi nhận được một số mục trong một bộ đệm.
Đối với đầy đủ tôi đính kèm mã kiểm tra, thực hiện sử dụng Delphi 5:
type
TWatcherThread = class(TThread)
private
fChangeHandle: THandle;
fDirHandle: THandle;
fShutdownHandle: THandle;
protected
procedure Execute; override;
public
constructor Create(ADirectoryToWatch: string);
destructor Destroy; override;
procedure Shutdown;
end;
constructor TWatcherThread.Create(ADirectoryToWatch: string);
const
FILE_LIST_DIRECTORY = 1;
begin
inherited Create(TRUE);
fChangeHandle := CreateEvent(nil, FALSE, FALSE, nil);
fDirHandle := CreateFile(PChar(ADirectoryToWatch),
FILE_LIST_DIRECTORY or GENERIC_READ,
FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
nil, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS or FILE_FLAG_OVERLAPPED, 0);
fShutdownHandle := CreateEvent(nil, FALSE, FALSE, nil);
Resume;
end;
destructor TWatcherThread.Destroy;
begin
if fDirHandle <> INVALID_HANDLE_VALUE then
CloseHandle(fDirHandle);
if fChangeHandle <> 0 then
CloseHandle(fChangeHandle);
if fShutdownHandle <> 0 then
CloseHandle(fShutdownHandle);
inherited Destroy;
end;
procedure TWatcherThread.Execute;
type
PFileNotifyInformation = ^TFileNotifyInformation;
TFileNotifyInformation = record
NextEntryOffset: DWORD;
Action: DWORD;
FileNameLength: DWORD;
FileName: WideChar;
end;
const
BufferLength = 65536;
var
Filter, BytesRead: DWORD;
InfoPointer: PFileNotifyInformation;
Offset, NextOffset: DWORD;
Buffer: array[0..BufferLength - 1] of byte;
Overlap: TOverlapped;
Events: array[0..1] of THandle;
WaitResult: DWORD;
FileName, s: string;
begin
if fDirHandle <> INVALID_HANDLE_VALUE then begin
Filter := FILE_NOTIFY_CHANGE_FILE_NAME or FILE_NOTIFY_CHANGE_DIR_NAME
or FILE_NOTIFY_CHANGE_SIZE or FILE_NOTIFY_CHANGE_LAST_WRITE;
FillChar(Overlap, SizeOf(TOverlapped), 0);
Overlap.hEvent := fChangeHandle;
Events[0] := fChangeHandle;
Events[1] := fShutdownHandle;
while not Terminated do begin
if ReadDirectoryChangesW (fDirHandle, @Buffer[0], BufferLength, TRUE,
Filter, @BytesRead, @Overlap, nil)
then begin
WaitResult := WaitForMultipleObjects(2, @Events[0], FALSE, INFINITE);
if WaitResult = WAIT_OBJECT_0 then begin
InfoPointer := @Buffer[0];
Offset := 0;
repeat
NextOffset := InfoPointer.NextEntryOffset;
FileName := WideCharLenToString(@InfoPointer.FileName,
InfoPointer.FileNameLength);
SetLength(FileName, StrLen(PChar(FileName)));
s := Format('[%d] Action: %.8xh, File: "%s"',
[Offset, InfoPointer.Action, FileName]);
OutputDebugString(PChar(s));
PByte(InfoPointer) := PByte(DWORD(InfoPointer) + NextOffset);
Offset := Offset + NextOffset;
until NextOffset = 0;
end;
end;
end;
end;
end;
procedure TWatcherThread.Shutdown;
begin
Terminate;
if fShutdownHandle <> 0 then
SetEvent(fShutdownHandle);
end;
////////////////////////////////////////////////////////////////////////////////
procedure TForm1.FormCreate(Sender: TObject);
begin
fThread := TWatcherThread.Create('D:\Temp');
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if fThread <> nil then begin
TWatcherThread(fThread).Shutdown;
fThread.Free;
end;
end;
Xóa một thư mục nào thực sự chỉ trả lại một sự thay đổi cho nó, không có gì cho các tập tin chứa trong nó. Nhưng nó có ý nghĩa, khi bạn đang xem xử lý của thư mục mẹ chỉ. Nếu bạn cần thông báo cho các thư mục con, bạn có thể cũng cần phải xem chúng.
Xin lỗi vì bình luận bị trì hoãn của tôi.Tôi đã sử dụng phiên bản đồng bộ trước đó (với cùng một vấn đề chính xác) nhưng đã chuyển sang phiên bản không đồng bộ vì tôi không tìm thấy cách để xóa hoàn toàn luồng. Mặc dù vậy, tôi đã bỏ lỡ một dòng quan trọng trong mã ví dụ (lệnh gọi chặn đến WaitForMultipleObjects, có thể bị hạn chế bởi một sự kiện thay đổi tập tin hoặc bởi một sự kiện kết thúc). Tôi chỉnh sửa câu hỏi cho phù hợp. (...) – jpfollenius
Ý của bạn là gì? Nếu bạn muốn lặp lại tất cả các tệp bằng FindFirst, FindNext: Trước đây tôi đã sử dụng cách tiếp cận này nhưng tôi muốn tránh (1) thời gian phát hiện thay đổi bị trì hoãn khi sử dụng các thư mục lớn và (2) tải I/O liên tục cho chuỗi chỉ mục làm chậm tất cả các hoạt động I/O khác. – jpfollenius
Đồng ý với bình luận thứ hai của bạn, nhưng như các tài liệu MSDN nói rằng bạn cần phải được chuẩn bị cho tràn bộ đệm bên trong, và trong trường hợp đó việc quét toàn bộ (lại) thư mục là cần thiết. – mghie