异步 ReadDirectoryChangesW - GetQueuedCompletionStatus 总是超时
正如听起来的那样,我正在尝试使用 IO Completion 进行异步 ReadDirectoryChangesW
,但它不起作用,具体来说,GetLastError
重复返回 258 (GetQueuedCompletionStatus
暂停)。
我有结构:
typedef struct dirinfo_struct
{
HANDLE hDirFH; // directory handle
OVERLAPPED Overlapped; // overlapped storage
int len_buffer; // buffer length
wchar_t* buffer; // buffer itself
wchar_t* directory_name; // target name
} dirinfo_t;
typedef struct dirmon_struct
{
HANDLE hDirOPPort; // handle to the IO port.
dirinfo_t* dirinfo; // pointer to the struct above.
} dirmon_t;
用于存储相关信息。这是初始化的:
dirinfo_t* t = malloc(1*sizeof(dirinfo_t));
dirmon_t* d = malloc(1*sizeof(dirmon_t));
dirinfo_init(t); // does t->buffer = malloc(8192*sizeof(wchar_t));
然后我创建我的目录句柄和 com 端口:
t->hDirFH = CreateFile(L"C:\\test",
FILE_LIST_DIRECTORY,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
d->dirinfo = t;
d->hDirOPPort = CreateIoCompletionPort(d->dirinfo->hDirFH,
NULL,
(ULONG_PTR)(d->dirinfo),
1);
然后我通过 d 将此信息传递到新线程。现在,在所说的新线程上,我有:
bResultQ = GetQueuedCompletionStatus(d->hDirOPPort, lpBytes,
(ULONG_PTR*)d->dirinfo,
lpOverlapped, 1000);
if ( bResultQ )
{
bResultR = ReadDirectoryChangesW(d->dirinfo->hDirFH,
(void*)d->dirinfo->buffer,
8192, 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,
lpReadDirBytes,
&d->dirinfo->Overlapped,
NULL );
}
else
{
printf("GetQueuedCompletionStatus(): Failed, ");
errorcode = GetLastError();
printf("Error Code %d\n", errorcode);
Sleep(500);
}
所以我将其设置为运行,并且我愉快地得到了超时(258 个错误),因为目录没有更改,所以我应该这样做。但是,即使我更改目录,我仍然收到错误消息;换句话说,这些更改没有被采纳。这让我相信我的设置不正确。
有什么想法吗?
注意事项:
- 具有讽刺意味的是,这最终将通过 pywin32 转换为 Python。然而,在我理解这在 C 中应该如何工作之前,我不会去那里。
- 同步
ReadDirectoryChangesW
不是一个选项。如果没有事件被触发,我需要该线程超时,以便它可以检查它是否应该仍在运行。 - 我专门用 C 编写,而不是 C++。
- FindFirstChangeNotification 等也不是一个选项 - 我不想不断比较目录列表来找出发生了什么变化。
其他注意事项:
- 目录存在,该句柄不为 NULL。对于兼容手柄也是如此。
- 所有内容都被传递到线程
我从代码项目中查看了 CDirectoryChangeWatcher ,但是除了使用 C++ 和更多线程之外,我看不出我在做什么不同的事情。如果我遗漏了什么,请随时指出!
输出(如果有帮助的话)基本上是重复的,无论我如何更改有问题的目录。
GetQueuedCompletionStatus(): Failed, Error Code 258
Exactly as it sounds, I'm attempting asynchronous ReadDirectoryChangesW
with IO Completion and it isn't working, specifically, GetLastError
repeatedly returns 258 (GetQueuedCompletionStatus
timeout).
I have structs:
typedef struct dirinfo_struct
{
HANDLE hDirFH; // directory handle
OVERLAPPED Overlapped; // overlapped storage
int len_buffer; // buffer length
wchar_t* buffer; // buffer itself
wchar_t* directory_name; // target name
} dirinfo_t;
typedef struct dirmon_struct
{
HANDLE hDirOPPort; // handle to the IO port.
dirinfo_t* dirinfo; // pointer to the struct above.
} dirmon_t;
for storing the relevant information. This is initialised:
dirinfo_t* t = malloc(1*sizeof(dirinfo_t));
dirmon_t* d = malloc(1*sizeof(dirmon_t));
dirinfo_init(t); // does t->buffer = malloc(8192*sizeof(wchar_t));
Then I create my Directory Handle and com port:
t->hDirFH = CreateFile(L"C:\\test",
FILE_LIST_DIRECTORY,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
d->dirinfo = t;
d->hDirOPPort = CreateIoCompletionPort(d->dirinfo->hDirFH,
NULL,
(ULONG_PTR)(d->dirinfo),
1);
Then I pass this information via d to a new thread. Now on said new thread I have:
bResultQ = GetQueuedCompletionStatus(d->hDirOPPort, lpBytes,
(ULONG_PTR*)d->dirinfo,
lpOverlapped, 1000);
if ( bResultQ )
{
bResultR = ReadDirectoryChangesW(d->dirinfo->hDirFH,
(void*)d->dirinfo->buffer,
8192, 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,
lpReadDirBytes,
&d->dirinfo->Overlapped,
NULL );
}
else
{
printf("GetQueuedCompletionStatus(): Failed, ");
errorcode = GetLastError();
printf("Error Code %d\n", errorcode);
Sleep(500);
}
So I set this off running and I merrily get timeouts (258 errors) as I should since the directory hasn't changed. However, even if I alter the directory, I'm still getting error messages; in other words those alterations are not being picked up. Which leads me to believe I've got this set up incorrectly.
Any ideas?
Caveats:
- Ironically, this will eventually be converted to Python via pywin32. However, until I understand how this is supposed to work in C, I'm not going there.
- Synchronous
ReadDirectoryChangesW
is not an option. If no events are fired, I need the thread this is on to timeout so it can check if it should still be running. - I am writing in C specifically, not C++.
FindFirstChangeNotification
etc not an option either - I don't want to continually be comparing directory listings to work out what has changed.
Other notes:
- The Directory exists, that handle is not NULL. Likewise for the comport handle.
- Everything's being passed to the thread
I've taken a look at CDirectoryChangeWatcher from code project, but use of C++ and many more threads aside, I can't see what I'm doing differently. Feel free to point it out if I'm missing something though!
Output, if it helps, is basically on repeat, no matter how much I alter the directory in question.
GetQueuedCompletionStatus(): Failed, Error Code 258
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我意识到发布代码墙通常被认为是可怕的,但这是我如何实现这一点的:
新结构:
分配方法:
main()
中的重要内容是这样做的:然后最后是我的等待线程。关键是:我没有传递重叠的结构。我传递的是一个包含
OVERLAPPED
以及相当数量的基于wchar_t
的存储的结构。由于我不完全理解的原因,这有效。 编辑请参阅此答案< /a>.我相信这里的数据区域充当重叠缓冲区。然后我们进入主线程本身。反复试验表明您应该同时调用 ReadDirectoryW 和 GetQueueCompletionStatus。 我认为这意味着我们不应该接触来自
ReadDirectoryChangeW
的缓冲区**除非*我们被告知我们可以通过GetQueue
>。不过,欢迎对该假设进行更正。所以,现在我们已经调用了这些函数,然后我们测试它们是否都返回 true。 丑陋的大警告如果你的参数设置正确
bResultR
总是返回true,或者在我看来是这样。然而,bResultQ
根据端口上是否有新数据而变化。因此,在这里我们从 ReadDirectoryChangesW 转换该缓冲区并从结构中访问信息。
否则,感谢托尼,您可以放心忽略 WAIT_TIMEOUT 错误,但其他任何情况都可能意味着您遇到了麻烦。
这完成了我认为是一个有效的示例。
一些注意事项:
8192
的空间,并且到处漏掉了一两个项目。所以我不指望这总是能解决所有问题。我的解决方案是每 100 个事件,验证文件树是否是您认为使用此方法时的情况。然而,一个无限更好的解决方案是不断枚举潜在的大树。I realise posting walls of code is generally considered horrendous, but here's how I got this working:
New structs:
Allocation methods:
The important stuff from
main()
does this:Then finally my waiting thread. Here's the key: I'm not passing an overlapped structure in. I'm passing in a structure containing an
OVERLAPPED
plus a fair amount ofwchar_t
based storage. For reasons I don't fully understand, this works. Edit see this answer. I believe the data region here acts as the overlapped buffer.Then we get onto the main thread itself. Trial and error suggests you're supposed to call both ReadDirectoryW AND GetQueueCompletionStatus. I think what this means is that we're supposed to not touch the buffer from
ReadDirectoryChangeW
**unless* we're told we can byGetQueue
. Corrections on that hypothesis welcome however.So, now we've called those functions, we then test that they both returned true. big ugly warning if you've got your parameters set up right
bResultR
always returns true, or so it seems to me.bResultQ
however varies depending on whether new data is on the port.So here we cast that buffer from
ReadDirectoryChangesW
and access the info from the struct.Otherwise, and thanks to Tony for this, you can safely ignore WAIT_TIMEOUT errors, but anything else probably means you're in trouble.
And that completes what I think is a working example.
Some notes:
8192
and missed off an item or two, here and there. So I don't expect this will always pick up everything. My solution would be to say every 100 events, verify the file tree is what you think it is if using this method. An infinitely better solution, however, to constantly enumerating the potentially large tree.注意:要正确捕获
GetQueuedCompletionStatus
中的错误,因为很难确定该函数是否实际返回,应按如下方式完成:示例:
摘自书中的示例(Windows 通过 C/C++)
请尝试在您的代码中实现此错误处理。
此外,“...调用
GetQueuedCompletionStatus
的线程以后进先出 (LIFO) 方式唤醒。”讲的是调用ReadFile或WriteFile时的参数,就像上面的注释一样,需要初始化这个结构体。
它看起来如下:
注意:您正在将指向结构的指针传递给
CreateIoCompletionPort
函数的dwCompletionKey
参数。在我正在查看的参考文献中,他们只是将一个常量(#define CK_FILE 1
)传递给它。它确实说你可以传递任何你喜欢的内容,因为操作系统不在乎。只是想指出这一点。Note: To catch errors from
GetQueuedCompletionStatus
properly, as it is difficult to determine that this function actually returned, should be done as follows:EXAMPLE:
Example taken from the book (Windows via C/C++)
Please try to implement this error handling in your code.
Also "... threads that call
GetQueuedCompletionStatus
are awakened in a last-in first-out (LIFO) fashion."It's talking about the parameter when you call ReadFile or WriteFile, just as a note to the above, which requires this structure to be initialized.
It looks as follows:
NOTE: You are passing a pointer to a struct to your
dwCompletionKey
parameter of yourCreateIoCompletionPort
function. In the reference I am looking at they merely pass a constant (#define CK_FILE 1
) to this. It does say you can pass whatever you like, as the OS doesn't care. Just wanted to point it out however.