Мне кажется, что вы смешиваете различные способы использования ReadDirectoryChangesW(), вы оба указываете флаг FILE_FLAG_OVERLAPPED при открытии каталога и предоставляете указатель на lpOverlapped, означающий, что вы хотите ожидать события в структуре и обрабатывать асинхронный ввод-вывод; и в то же время вы вызываете ReadDirectoryChangesW() в цикле рабочего потока. Я бы сначала попробовал еще раз, установив для lpOverlapped значение nil, так как у вас есть выделенный поток и вы можете использовать синхронный режим.
В документации по ReadDirectoryChangesW() Функция API, описаны различные способы ее использования. Обратите внимание, что также возможно переполнение буфера, поэтому события изменения в любом случае могут быть потеряны. Возможно, вам следует переосмыслить свою стратегию, полагаясь исключительно на эту функцию, сравнение снимков содержимого каталога также может сработать.
Изменить:
Ваш отредактированный код выглядит лучше. Однако в моих тестах функция ReadDirectoryChangesW() работала, как заявлено, либо в возвращаемом буфере было несколько записей данных, либо для обработки требовалось более одного буфера. Это зависит от времени, после достижения точки останова в Delphi я получаю несколько записей в одном буфере.
Для полноты прикрепляю тестовый код, реализованный на 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;
Удаление каталога действительно возвращает только одно изменение для него и ничего для содержащихся в нем файлов. Но это имеет смысл, поскольку вы смотрите только на дескриптор родительского каталога. Если вам нужны уведомления для подкаталогов, вам, вероятно, также нужно следить за ними.
person
mghie
schedule
14.05.2009