FILE_NOTIFY_INFORMATION
составляет не менее 16 байтов (для длинных имен файлов 0 wchar_t
s), и вы говорите ReadDirectoryChangesW
, что у вас есть только 20 байтов в буфере (nBufferLength
) - поэтому перекрывающиеся результаты будут иметь проблемы с подгонкой,Используйте sizeof(_resultData)
вместо ResultDataSize
для nBufferLength
- но я думаю, вам следует значительно увеличить размер буфера. 16 * 20 байт не так много, когда вещи начинают происходить.
Также обратите внимание, что вы не можете использовать _resultData[ index+1 ]
, чтобы перейти к следующему результату. FILE_NOTIFY_INFORMATION
- переменная длина, следующий FILE_NOTIFY_INFORMATION
на NextEntryOffset
байтов вперед (0 означает, что вы достигли последнего перекрывающегося результата).
Вам также необходимо создать и назначить дескриптор события (hEvent
) в вашей структуре OVERLAPPED
, чтобы GetOverlappedResult()
работал.
Вот пример с этими вещами. Я использую WaitForSingleObject
, так как у меня есть только это, чтобы ждать, но эта часть должна быть наименьшей из проблем.
#include <Windows.h>
#include <iomanip>
#include <iostream>
#include <string>
#include <stdexcept>
#include <tuple>
#include <utility>
#include <vector>
// A base class for handles with different invalid values.
template<std::uintptr_t hInvalid>
class Handle {
public:
Handle(const Handle&) = delete;
Handle(Handle&& rhs) :
hHandle(std::exchange(rhs.hHandle, hInvalid))
{}
Handle& operator=(const Handle&) = delete;
Handle& operator=(Handle&& rhs) {
std::swap(hHandle, rhs.hHandle);
return *this;
}
// converting to a normal HANDLE
operator HANDLE () { return hHandle; }
protected:
Handle(HANDLE v) : hHandle(v) {
// throw if we got an invalid handle
if (hHandle == reinterpret_cast<HANDLE>(hInvalid))
std::runtime_error("invalid handle");
}
~Handle() {
if (hHandle != reinterpret_cast<HANDLE>(hInvalid)) CloseHandle(hHandle);
}
private:
HANDLE hHandle;
};
using InvalidNullptrHandle = Handle<reinterpret_cast<std::uintptr_t>(nullptr)>;
using InvalidHandleValueHandle =
Handle<reinterpret_cast<std::uintptr_t>(INVALID_HANDLE_VALUE)>;
// A class for directory handles
class DirectoryHandleW : public InvalidHandleValueHandle {
public:
DirectoryHandleW(const std::wstring& dir) :
Handle(
::CreateFileW(
dir.c_str(), FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS |
FILE_FLAG_OVERLAPPED, NULL)
)
{}
};
// A class for event handles
class EventHandle : public InvalidNullptrHandle {
public:
EventHandle() :
Handle(::CreateEvent(nullptr, true, false, nullptr))
{}
};
// FILE_NOTIFY_INFORMATION action names
wchar_t const* get_action(DWORD a) {
static wchar_t const* const Actions[FILE_ACTION_RENAMED_NEW_NAME + 1] = {
L"Unknown action",
L"ADDED",
L"REMOVED",
L"MODIFIED",
L"RENAMED_OLD_NAME",
L"RENAMED_NEW_NAME"
};
if (a > FILE_ACTION_RENAMED_NEW_NAME) a = 0;
return Actions[a];
}
// A function to get a vector of "Action, Filename" pairs
std::vector<std::pair<wchar_t const*, std::wstring>>
GetDirectoryChangesResultW(HANDLE hDir, OVERLAPPED& ovl, DWORD* buffer) {
std::vector<std::pair<wchar_t const*, std::wstring>> retval;
FILE_NOTIFY_INFORMATION* fni = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(buffer);
DWORD ovlBytesReturned;
if (::GetOverlappedResult(hDir, &ovl, &ovlBytesReturned, FALSE)) {
do {
retval.emplace_back(
get_action(fni->Action),
std::wstring
{ fni->FileName,
fni->FileName + fni->FileNameLength / sizeof(wchar_t) }
);
if (fni->NextEntryOffset == 0) break; // last entry
fni = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(
reinterpret_cast<char*>(fni) + fni->NextEntryOffset
);
} while (true);
}
return retval;
}
int main()
{
try {
DWORD buffer[4062]; // DWORD-aligned
DirectoryHandleW hDir(L"C:\\Users\\Ted\\Testing");
EventHandle hEv;
while (true) {
OVERLAPPED ovl{};
ovl.hEvent = hEv;
BOOL rdc = ::ReadDirectoryChangesW(
hDir,
buffer,
std::size(buffer),
TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME| FILE_NOTIFY_CHANGE_DIR_NAME|
FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_SIZE|
FILE_NOTIFY_CHANGE_LAST_WRITE| FILE_NOTIFY_CHANGE_LAST_ACCESS|
FILE_NOTIFY_CHANGE_CREATION| FILE_NOTIFY_CHANGE_SECURITY,
NULL,
&ovl,
NULL
);
if (rdc) {
if (WaitForSingleObject(hEv, INFINITE) == WAIT_OBJECT_0) {
auto res = GetDirectoryChangesResultW(hDir, ovl, buffer);
for (auto const&[action, filename] : res) {
std::wcout << action << L" " << filename << L"\n";
}
}
}
else {
std::wcout << L"ReadDirectoryChangesW failed\n" << std::endl;
}
}
}
catch (const std::exception& ex) {
std::cout << ex.what() << "\n";
}
}