При вызове ReadDirectoryChangesW только первый вызов возвращает любые изменения (как синхронизированные, так и асинхронные).
Ниже приведена минимальная программа, которая использует ReadDirectoryChangesW
, У меня проблема в том, что только первый звонок GetQueuedCompletionStatus
возвращается. Второй раз в цикле он блокируется навсегда, независимо от того, сколько изменений внесено в каталог.
Я также пытался использовать синхронную версию и у меня точно такая же проблема.
#include <array>
#include <cassert>
#include <iostream>
#include <Windows.h>
int main() {
// Open the directory to monitor.
HANDLE dir = ::CreateFileA(
"G:\\Program Files (x86)\\Steam\\steamapps\\common\\eve online"
, FILE_LIST_DIRECTORY
, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED
, NULL
);
if (dir == INVALID_HANDLE_VALUE) {
std::cout << "Failed to open directory for change notifications!\n";
return 1;
}
// Setup IOCP.
HANDLE iocp = ::CreateIoCompletionPort(
dir
, NULL
, NULL
, 1
);
// Monitor.
while (true) {
std::array<char, 1024 * 8> buf;
DWORD bytes_read;
OVERLAPPED overlapped;
std::memset(&overlapped, 0, sizeof(overlapped));
BOOL result = ::ReadDirectoryChangesW(
dir
, &buf.front()
, buf.size()
, false
, FILE_NOTIFY_CHANGE_FILE_NAME // Includes file creation.
, &bytes_read
, &overlapped
, NULL
);
if (result == FALSE) {
DWORD error = ::GetLastError();
std::cout << "Call to ReadDirectoryChangesW failed! " << error << "\n";
return 1;
}
// Wait for completion.
ULONG_PTR key;
LPOVERLAPPED overlapped_result;
result = ::GetQueuedCompletionStatus(
iocp
, &bytes_read
, &key
, &overlapped_result
, INFINITE
);
if (result == FALSE) {
std::cout << "Call to GetQueuedCompletionStatus failed!\n";
return 1;
}
// Print results!
for (FILE_NOTIFY_INFORMATION *fni =
reinterpret_cast<FILE_NOTIFY_INFORMATION *>(&buf.front());
;
fni = reinterpret_cast<FILE_NOTIFY_INFORMATION *>(
reinterpret_cast<char *>(fni) + fni->NextEntryOffset)) {
std::wstring filename(fni->FileName, fni->FileName + fni->FileNameLength);
std::wcout << "Got change: " << filename.c_str() << "\n";
if (fni->NextEntryOffset == 0) break;
}
}
}
3 ответа
Несколько проблем.
Сначала вы пытаетесь вывести многобайтовые строковые литералы в wcout
, Вы должны превратить их в широкие струны, добавив L.
Во-вторых, FileNameLength
Переменная представляет длину имени в байтах, а не в символах. Вы должны разделить его на 2, чтобы получить количество символов.
Проблема в том, что ваша логика печати вызывает переполнение буфера, потому что fni-> FileNameLength в байтах, а не в символах. Случайное повреждение памяти объясняет, почему я получил результаты, отличные от вас.
Исправление просто так:
имя файла std::wstring (fni->FileName, fni->FileName + fni->FileNameLength / sizoeof(fni->FileName[0]));
Как вы это компилируете? При использовании Visual Studio не удается скомпилировать, поскольку третий параметр GetQueuedCompletionStatus введен неправильно. Параметр должен быть указателем на указатель на ULONG, а не указателем на ULONG. Когда я изменил объявление переменной "ключ" на
Ключ ULONG_PTR;
программы работают правильно.