![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
ReadDirectoryChangesW
Dennis I. Komarov © (2010-10-11 11:59:00)При повторном вызове функции в асинхронном режиме что происходит в системе, если изменений в директории не было? Функция возвращает правду.
Return Values
If the function succeeds, the return value is nonzero. For synchronous calls, this means that the operation succeeded. For asynchronous calls, this indicates that the operation was successfully queued.
Игорь Шевченко (2010-10-11 15:16:00) [21]Я где-то так делаю
while not Terminated do
begin
if not ReadDirectoryChangesW (FDirectoryHandle, FBuffer,
FOLDER_MONITOR_BUFFER_SIZE, false, FILE_NOTIFY_CHANGE_FILE_NAME,
@BytesRead, @FOverlapped, nil) then
begin
DoOnError(SysErrorMessage(GetLastError));
Terminate;
Break; //Невозможно поставить запрос наблюдения каталога в очередь
end;
WaitResult := WaitForMultipleObjects(2, @FEventsToWait, false, INFINITE);
if WaitResult = WAIT_OBJECT_0 then
begin
Terminate;
Break; //Получен внешний запрос на окончание наблюдения
end
else if WaitResult <> WAIT_OBJECT_0+1 then
begin
DoOnError(SysErrorMessage(GetLastError));
Terminate;
Break; //Неизвестная ошибка
end
else
begin
//Закончилась операция чтения изменений в каталоге
if not GetOverlappedResult (FDirectoryHandle, FOverlapped, BytesRead,
false) then
begin
DoOnError(SysErrorMessage(GetLastError));
Terminate;
Break; //Неизвестная ошибка при попытке получения результата окончания
//асинхронной операции ввода-вывода
end;
// Сейчас в буфере находится BytesRead байт информации об изменениях в
// каталоге
Files := TStringList.Create;
try
ParseNotificationBuffer (FBuffer, Files);
for I:=0 to Pred(Files.Count) do
DoOnNewFile(Files[I]);
finally
Files.Free;
end;
end;
end;
Dennis I. Komarov (2010-10-11 15:10:00) [22]
> if ReadDirectoryChangesW(hDir, lpBuf, ...
> while not Terminated do begin
> dwWaitStatus := WaitForSingleObject(lpOverlapped.hEvent,
> 1000);
> if dwWaitStatus <> WAIT_TIMEOUT then begin
> //Копируем lpBuf в lpBuf2 (временный буфер)
> ReadDirectoryChangesW(hDir, lpBuf...
> Ptr:=lpBuf2;
> while PFileNotifyInformation(Ptr).NextEntryOffset <>
> 0 do begin
> ...
Во время выполнения копирования могут произойти изменения в директории. Получается они будут потеряны :(
Dennis I. Komarov (2010-10-11 14:43:00) [23]
> Тоже поставь проверку на результат выполнения ReadDirectoryChangesW,
> он не обязательно равен true
ес-но, это наброски в on-line :)
Вариант (2010-10-11 14:39:00) [24]И да по варианту в [10]
> //Копируем lpBuf в lpBuf2 (временный буфер)
> ReadDirectoryChangesW(hDir, lpBuf...
Тоже поставь проверку на результат выполнения ReadDirectoryChangesW, он не обязательно равен true
Вариант (2010-10-11 14:32:00) [25]
> Dennis I. Komarov © (11.10.10 14:01) [10]
> Но, в процессе lpBuf -> lpBuf2 так же могут быть изменения,
> которые будут потеряны...
По той части кода, что во втором варианте, lpBuf может быть изменен сразу после вызвова ReadDirectoryChangesW, ну и пусть, ты уже нужное мкопировал в другой буфер и начал его обработку. И копировать в этот временный буфер будешь только при слудующей сработке hEvent, что по коду должно происходить внутри твоего
>
> if dwWaitStatus <> WAIT_TIMEOUT then begin
Так что потерь из-за логики обработки в этом случае не вижу.
Dennis I. Komarov (2010-10-11 14:28:00) [26]
> > while not Terminated do begin
> > ZeroMemory(lpBuf, BufSize);
> (Чистим память, а какое-то мгновение назад туда была положена
> инфо)
>
Секунду. Это в первом варианте. Там никак не получим такого. В [10] такого нету...
Вариант (2010-10-11 14:19:00) [27]
> Dennis I. Komarov © (11.10.10 14:01) [10]
> if dwWaitStatus <> WAIT_TIMEOUT then begin
> //Копируем lpBuf в lpBuf2 (временный буфер)
Кстати в этом варианте уже проблемы с потрей нет.
Вариант (2010-10-11 14:15:00) [28]
> Dennis I. Komarov © (11.10.10 14:01) [10]
Да, потому что ты так написал. Изменение буфера может произойти после вызова ReadDirectoryChangesW, но до окончания операции. После окончания операции изменения в буфере не произуйдут, пока ты их не изменишь
- а это смотрим твой вариант 1
> while not Terminated do begin
> ZeroMemory(lpBuf, BufSize);
(Чистим память, а какое-то мгновение назад туда была положена инфо)
Но если и не чистить память, то после нового вызова ReadDirectoryChangesW так же может произойти перезапись буфера. У тебя может получится примерно такая ситуация, как будто два потока одновременно пишут в один буфер. Даже если и одновременно один пишет, а другой читает - все равно нет согласованности. Для того синхронизацию и делают, что бы разделить доступ к одному ресурсу.
Или другая аналогия - представь критическую секцию - в одном потоке ты вызвал код, для доступа к буфера внутри критической секции, а в другом обращаешься к буферу без критической секции. Смысла в вызове критической секции нет. В данном случае Event играет не только роль сигнализатора - что есть событие, но и роль разделителя доступа к общему ресурсу.
Dennis I. Komarov (2010-10-11 14:01:00) [29]
if ReadDirectoryChangesW(hDir, lpBuf, ...
while not Terminated do begin
dwWaitStatus := WaitForSingleObject(lpOverlapped.hEvent, 1000);
if dwWaitStatus <> WAIT_TIMEOUT then begin
//Копируем lpBuf в lpBuf2 (временный буфер)
ReadDirectoryChangesW(hDir, lpBuf...
Ptr:=lpBuf2;
while PFileNotifyInformation(Ptr).NextEntryOffset <> 0 do begin
...
Но, в процессе lpBuf -> lpBuf2 так же могут быть изменения, которые будут потеряны...
P.S.
> This structure should always be initialized to zero before
> it is used in a function call. If it is not, the function
> may fail and return ERROR_INVALID_PARAMETER.
Принято
Вариант (2010-10-11 13:26:00) [30]
> Dennis I. Komarov © (11.10.10 12:54) [8]
Я бы установил перед первым использованием поля lpOverlapped в 0.
Возможно этого и не надо, так как используется нами только поле hEvent, устанавливаемое явно. Но я не знаю, что использует функция или система, какие еще поля анализируются, а потому пользуюсь рекомедацией MSDN
> This structure should always be initialized to zero before
> it is used in a function call. If it is not, the function
> may fail and return ERROR_INVALID_PARAMETER.
>
> WaitForSingleObject(lpOverlapped.hEvent, 1000)
- тайм-аут я так понимаю задан для периодической проверки флага Terminated. О том что необходимо завершить работу потоку можно сигнализировать и другими способами - например другим Event или QueueUserAPC.Соответственно надо заменить WaitForSingleObject на соответствующий вызов другой функции. Но даже и при использовании тайм-аута вполне можно избежать повторного ненужного вызова ReadDirectoryChangesW. Ибо в том варианте, как производится вызов в твоем коде, возможна ситуация, когда будут потеряны события мониторинга.
Dennis I. Komarov (2010-10-11 12:54:00) [31]Код для критики (кроме повторного вызова) :)
unit dirchange;
interface
uses
Classes, Windows, SysUtils;
const
BufSize: Integer = 16 * 1024;
type
PFileNotifyInformation = ^TFileNotifyInformation;
TFileNotifyInformation = record
NextEntryOffset: DWORD;
Action: DWORD;
FileNameLength:DWORD;
FileName: array [0..255] of WideChar;
end;
TThreadDirChange = class(TThread)
private
Directory: string;
FILE_NOTIFY_CHANGE: Cardinal;
protected
procedure Execute; override;
end;
implementation
procedure TThreadDirChange.Execute;
var
lpOverlapped: OVERLAPPED;
dwWaitStatus: Cardinal;
hDir: THandle;
lpBuf, Ptr: Pointer;
cbReturn: Cardinal;
FileName: PWideChar;
sTime: _SYSTEMTIME;
begin
hDir:=CreateFile(PAnsiChar(IncludeTrailingBackSlash(Directory)), 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);
try
if hDir <> INVALID_HANDLE_VALUE then begin
lpOverlapped.hEvent := CreateEvent(nil, true, false, nil);
GetMem(lpBuf, BufSize);
try
while not Terminated do begin
ZeroMemory(lpBuf, BufSize);
if ReadDirectoryChangesW(hDir, lpBuf, BufSize, true, FILE_NOTIFY_CHANGE, @cbReturn, @lpOverlapped, nil) then begin
dwWaitStatus := WaitForSingleObject(lpOverlapped.hEvent, 1000);
if dwWaitStatus <> WAIT_TIMEOUT then begin
Ptr:=lpBuf;
while PFileNotifyInformation(Ptr).NextEntryOffset <> 0 do begin
GetLocalTime(sTime);
GetMem(FileName, PFileNotifyInformation(Ptr).FileNameLength + 2);
try
lstrcpynW(FileName, PFileNotifyInformation(Ptr).FileName, PFileNotifyInformation(Ptr).FileNameLength div 2 + 1);
// Обрабатываем что произошло...
// Отправляем основному потоку (если надо)
finally
FreeMem(FileName);
end;
Inc(Cardinal(Ptr), PFileNotifyInformation(Ptr).NextEntryOffset);
end;
end;
end;
end;
finally
FreeMem(lpBuf);
end;
end;
finally
CloseHandle(hDir);
end;
end;
end.
Dennis I. Komarov (2010-10-11 12:37:00) [32]
> посему повторная постановка заявки не нужна
Да с этим я не спорю... Я про систему интересовался ;)
Сергей М. (2010-10-11 12:36:00) [33]но даже несмотря на то что система не обидится, отвал по таймауту нельзя рассматривать как исполнение заявки, посему повторная постановка заявки не нужна
Сергей М. (2010-10-11 12:32:00) [34]
> отвалили по time-out... вызвали еще раз... система обидится?
Не должна.
Dennis I. Komarov (2010-10-11 12:21:00) [35]
> А какой смысл ее повторно вызывать ?
Ну да, но вопрос был - как на это отреагирует система... :)
> Заявка поставлена - жди результатов ее выполнения ..
Подождали... отвалили по time-out... вызвали еще раз... система обидится?
Сергей М. (2010-10-11 12:15:00) [36]
> корректно ли вызывать повторно функцию, если изменений после
> первого вызова не произошло?
А какой смысл ее повторно вызывать ?
Заявка поставлена - жди результатов ее выполнения ..
Dennis I. Komarov (2010-10-11 12:08:00) [37]При повторном вызове "заявка на изменение" будет новая? Т.е. суть такая: корректно ли вызывать повторно функцию, если изменений после первого вызова не произошло?
Сергей М. (2010-10-11 12:02:00) [38]
> Функция возвращает правду
> что происходит в системе .. ?
Написано же что происходит:
operation was successfully queued