ReadDirectoryChangesW仅将单个事件放在FILE_NOTIFY_INFORMATION缓冲区中 [英] ReadDirectoryChangesW only places single event in the FILE_NOTIFY_INFORMATION buffer
问题描述
我有一个问题,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
);
上述之后,我使用WaitForMultipleObjects
等待_ovl.hEvent
.我使用了多个对象,因为总是有我要告诉监视线程退出的事件.
如果通知了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
}
}
现在您可以看到,在处理 我可以通过让第二个线程执行处理FILE_CHANGES 部分中的所有信息来部分地解决这个问题.但这只会通过发出整个开始请求-> 等待-> 捡起-> 重新开始事件来减少错过事件的机会例程要快一些.它实际上并没有提供100%的覆盖率,仍然有一段时间没有任何 我在互联网上阅读了很多书,发现经常提到两种解决方案: 因此问题是:如何获取 要重命名文件,发生了两个操作: 我有一个问题,就是ReadDirectoryChangesW会丢失事件. 要捕获诸如复制两个文件并删除两个文件的事件,例如,首先将 TESTA.txt 和 TESTB.txt 复制到目录 测试代码如下: 摘要: GetOverlappedResult函数报告的结果是
指定句柄的上次重叠操作. 因此,重命名文件是一项重叠(重命名)操作,而复制文件是一项重叠(复制)操作.但是,复制两个文件是两个重叠的(复制)操作.因此,它需要两次调用 I have a problem that I did a lot of googling and the bellow function arguments seem correct according to my searches, but nobody knows for certain. I start watching like this. After the above, I wait for the If the I should note at this point, that What never get is multiple file changes per event. This is a problem, the whole code looks like this (pseudocode) Now you can see that I restart the request for I could fist this partially by having second thread to do the Process all info from FILE_CHANGES part. But that only reduces chance of missing the events by making the whole start request -> wait -> pick up -> restart event routine a bit faster. It does not actually provide 100% coverage, there is still a moment where no I've been reading a lot on the internet and found two solutions being mentioned often: Thus the question is: How do I get For renaming a file there are two actions happened: I have a problem that ReadDirectoryChangesW keeps missing events. To capture events like copy two files and delete two files, for example, firstly I copy TESTA.txt and TESTB.txt to directory Test code as follows: Summary: GetOverlappedResult function: The results reported by the GetOverlappedResult function are those of
the specified handle's last overlapped operation. So renaming a file is an overlapped (rename) operation and copying a file is an overlapped (copy) operation. However copying two files are two overlapped (copy) operations. So it requires calling 这篇关于ReadDirectoryChangesW仅将单个事件放在FILE_NOTIFY_INFORMATION缓冲区中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!MyFileInfoStruct
数组之前,我重新启动了对ReadDirectoryChangesW
的请求.但是问题是,如果复制了两个以上的文件,则当我正在处理前一个文件时,该事件将注册第二个文件,但是随后的更改将被忽略,直到我拿起"最后一个更改并重新启动事件./p>
ReadDirectoryChangesW
请求待处理.
FILE_NOTIFY_INFORMATION[]
的大小.这对我不起作用,Windows只会在其中放置一个事件ReadDirectoryChangesW
和GetOverlappedResult
来继续在FILE_NOTIFY_INFORMATION[]
缓冲区中添加文件更改,直到我通过调用GetOverlappedResult
拾取"结果为止?这有可能吗?有没有人设法将多个结果放入一个缓冲区?ReadDirectoryChanges
和 GetOverlappedResult
,就像您已经做过的.
D:\testFolder
,然后删除他们俩.我可以通过调用 ReadDirectoryChanges
来获取所有事件和 GetOverlappedResult
while
循环中的a>.这两个函数在四事件之间都调用了四次.#include <windows.h>
#include <vector>
using namespace std;
typedef struct TEST_INFO {
DWORD NextEntryOffset;
DWORD Action;
DWORD FileNameLength;
WCHAR FileName[100];
}_TEST_INFO;
int main()
{
BOOL _watchRequestResult = false;
OVERLAPPED _ovl = { 0 };
_ovl.hEvent = ::CreateEventA(NULL, TRUE, FALSE, NULL);
HANDLE _directoryHandle = ::CreateFileA("d:\\testFolder", 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 ResultDataSize = 100;
_TEST_INFO _resultData[ResultDataSize] = { 0 };
while (true)
{
_watchRequestResult = ::ReadDirectoryChangesW(
_directoryHandle,
(LPVOID)_resultData,
ResultDataSize * sizeof(_TEST_INFO),
TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME,
NULL,
&_ovl,
NULL
);
DWORD _ovlBytesReturned = 0;
if (::GetOverlappedResult(_directoryHandle, &_ovl, &_ovlBytesReturned, TRUE))
{
int byteIndex = 0;
while (TRUE)
{
_TEST_INFO* info = reinterpret_cast<_TEST_INFO*>(reinterpret_cast<char*>(&_resultData[0]) + byteIndex);
byteIndex += info->NextEntryOffset;
wprintf(L"File name: %s, ", info->FileName);
printf("Action: ");
switch (info->Action)
{
case FILE_ACTION_ADDED:
printf("Added \n");
break;
case FILE_ACTION_REMOVED:
printf("Removed \n");
break;
case FILE_ACTION_MODIFIED:
printf("Modified \n");
break;
case FILE_ACTION_RENAMED_OLD_NAME:
printf("Rename old name \n");
break;
case FILE_ACTION_RENAMED_NEW_NAME:
printf("Rename new name \n");
break;
}
// If next entry index is 0, it means there is no next entry
if (info->NextEntryOffset == 0)
{
break;
}
}
}
}
getchar();
}
GetOverlappedResult
而不是一次.ReadDirectoryChangesW
keeps missing events.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
. I use multiple objects, because there's always also the event I ise to tell the watch thread to quit.ovl.hEvent
is notified, I do this: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
is not always 0 - if I rename a file, I correctly get two entries in _resultData
, one for new filename and one for old. 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
before I process the MyFileInfoStruct
array. But the problem is, if more than two files are copied, the second file is registered by the event while I am processing the previous one, but the subsequent changes are ignored until I "pick up" the last change and restart the event.ReadDirectoryChangesW
request is pending.
FILE_NOTIFY_INFORMATION[]
. this doesn't work for me, windows only puts one event thereReadDirectoryChangesW
and GetOverlappedResult
to keep adding file changes in the FILE_NOTIFY_INFORMATION[]
buffer until I "pick up" the results by calling GetOverlappedResult
? is this even possible? Has anyone managed to get multiple results into one buffer?FILE_ACTION_RENAMED_OLD_NAME
and FILE_ACTION_RENAMED_NEW_NAME
. These two action events you can retrieve via once calling of ReadDirectoryChanges
and GetOverlappedResult
as you already have done.
D:\testFolder
, then delete them both. I can get all events by calling ReadDirectoryChanges
and GetOverlappedResult
in a while
loop. Both functions called by four times for fours events.#include <windows.h>
#include <vector>
using namespace std;
typedef struct TEST_INFO {
DWORD NextEntryOffset;
DWORD Action;
DWORD FileNameLength;
WCHAR FileName[100];
}_TEST_INFO;
int main()
{
BOOL _watchRequestResult = false;
OVERLAPPED _ovl = { 0 };
_ovl.hEvent = ::CreateEventA(NULL, TRUE, FALSE, NULL);
HANDLE _directoryHandle = ::CreateFileA("d:\\testFolder", 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 ResultDataSize = 100;
_TEST_INFO _resultData[ResultDataSize] = { 0 };
while (true)
{
_watchRequestResult = ::ReadDirectoryChangesW(
_directoryHandle,
(LPVOID)_resultData,
ResultDataSize * sizeof(_TEST_INFO),
TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME,
NULL,
&_ovl,
NULL
);
DWORD _ovlBytesReturned = 0;
if (::GetOverlappedResult(_directoryHandle, &_ovl, &_ovlBytesReturned, TRUE))
{
int byteIndex = 0;
while (TRUE)
{
_TEST_INFO* info = reinterpret_cast<_TEST_INFO*>(reinterpret_cast<char*>(&_resultData[0]) + byteIndex);
byteIndex += info->NextEntryOffset;
wprintf(L"File name: %s, ", info->FileName);
printf("Action: ");
switch (info->Action)
{
case FILE_ACTION_ADDED:
printf("Added \n");
break;
case FILE_ACTION_REMOVED:
printf("Removed \n");
break;
case FILE_ACTION_MODIFIED:
printf("Modified \n");
break;
case FILE_ACTION_RENAMED_OLD_NAME:
printf("Rename old name \n");
break;
case FILE_ACTION_RENAMED_NEW_NAME:
printf("Rename new name \n");
break;
}
// If next entry index is 0, it means there is no next entry
if (info->NextEntryOffset == 0)
{
break;
}
}
}
}
getchar();
}
GetOverlappedResult
two times instead of one.