我有一个问题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
using 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部分的所有信息来部分解决这个问题。但这只会通过发出整个启动请求来减少错过事件的机会->等待->拾取->重新启动事件例程更快一些它实际上并没有提供 100% 的覆盖率,仍然存在没有ReadDirectoryChangesW
待处理请求的时刻。
我在互联网上阅读了很多内容,发现经常提到两种解决方案:
- 使用单独的线程来处理文件更改(我已经这样做了)
- 增加
FILE_NOTIFY_INFORMATION[]
. 这对我不起作用,Windows 只在那里放置一个事件
因此问题是:我如何获得ReadDirectoryChangesW
并GetOverlappedResult
继续在FILE_NOTIFY_INFORMATION[]
缓冲区中添加文件更改,直到我通过调用“拾取”结果GetOverlappedResult
?这甚至可能吗?有没有人设法将多个结果放入一个缓冲区?