У меня проблема с тем, что ReadDirectoryChangesW
сохраняет пропущенные события.
Я много гуглил, и приведенные ниже аргументы функции кажутся правильными в соответствии с моими поисками, но никто точно не знает. Я начинаю смотреть вот так.
BOOL _watchRequestResult = false;
OVERLAPPED _ovl = { 0 };
_ovl.hEvent = ::CreateEventA(NULL, TRUE, FALSE, NULL);
_directoryHandle = ::CreateFileA("some path here", FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
// This should be quite enough to fit multiple file events
static constexpr DWORD ResultDataLength = 10000;
// Byte size used for winapi calls and memcpy during move operation
static constexpr DWORD ResultDataByteSize = ResultDataLength * sizeof(FILE_NOTIFY_INFORMATION);
FILE_NOTIFY_INFORMATION _resultData[ResultDataLength] = { 0 };
_watchRequestResult = ::ReadDirectoryChangesW(
_directoryHandle,
(LPVOID)_resultData,
ResultDataByteSize,
TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME,
NULL,
&_ovl,
NULL
);
После всего вышесказанного я жду _ovl.hEvent
, используя WaitForMultipleObjects
. Я использую несколько объектов, потому что всегда есть событие, которое я должен сообщить потоку наблюдения о выходе.
Если уведомление ovl.hEvent
получено, я делаю это:
DWORD _ovlBytesReturned = 0;
// Imagine some struct that I use to pass the file info, not important how it looks
std::vector<MyFileInfoStruct> results;
if (::GetOverlappedResult(_directoryHandle, &_ovl, &_ovlBytesReturned, TRUE))
{
int byteIndex = 0;
bool previousWasRename = false;
const int minSize = min(ResultDataLength, _ovlBytesReturned);
while (byteIndex < minSize)
{
FILE_NOTIFY_INFORMATION* info = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(reinterpret_cast<char*>(&_resultData[0]) + byteIndex);
byteIndex += info->NextEntryOffset;
// read the stuff in the info
results.push_back(MyFileInfoStruct::FromFileInfo(info));
// If next entry index is 0, it means there is no next entry
if (info->NextEntryOffset == 0)
{
break;
}
}
}
// if file is renamed, merge new name and old name to same result. However rename works to give me two FILE_NOTIFY_INFORMATION that both contain expected data
MergeResultRename(results)
// results is always 1 item long
Я должен обратите внимание, что info->NextEntryOffset
не всегда 0 - если я переименую файл, я правильно получу две записи в _resultData
, одну для нового имени файла и одну для старого.
Чего никогда не получить, так это нескольких изменений файла на событие. Это проблема, весь код выглядит следующим образом (псевдокод)
Let EVENTS be an array of HANDLE event objects
Let FILE_CHANGES be a buffer of file changes
while(shouldBeWatching)
{
Wait for events from previous iteration stored in EVENTS array. Skip on first iteration.
if(event has fired)
{
if(event that fired is ovl.hEvent)
{
Put file changes from the event that fired into FILE_CHANGES array (seen in 2nd code sample above)
Delete and close all handles related to the event:
Close directory handle
Close ovl.hEvent
}
else
{
Close everything and quit thread.
}
}
Start new request (seen above in 1st code sample)
if(FILE_CHANGES is not empty)
{
Process all info from FILE_CHANGES
}
}
Теперь вы видите, что я перезапускаю запрос на ReadDirectoryChangesW
до Я обрабатываю массив MyFileInfoStruct
, Но проблема в том, что если копируется более двух файлов, второй файл регистрируется событием, пока я обрабатываю предыдущий, но последующие изменения игнорируются, пока я не "заберу" последнее изменение и перезапущу событие.
Я мог бы частично это сделать, имея второй поток для обработки всей информации из части FILE_CHANGES . Но это только снижает вероятность пропуска событий, поскольку весь запрос на запуск -> wait -> забирает -> событие перезапуска рутина немного быстрее. На самом деле он не обеспечивает 100% покрытия, все еще есть момент, когда запрос ReadDirectoryChangesW
не рассматривается.
Я много читал о inte rnet и нашел два решения, которые часто упоминаются:
- Использовать отдельный поток для обработки изменений файла (я это уже сделал)
- Увеличить размер
FILE_NOTIFY_INFORMATION[]
. это не работает для меня, windows помещает туда только одно событие
Таким образом, вопрос заключается в следующем: как мне получить ReadDirectoryChangesW
и GetOverlappedResult
, чтобы продолжать добавлять изменения файлов в FILE_NOTIFY_INFORMATION[]
Буфер, пока я "забрать" результаты, позвонив GetOverlappedResult
? это вообще возможно? Кому-нибудь удалось получить несколько результатов в один буфер?