异步ReadDirectoryChangesW – GetQueuedCompletionStatus总是超时

正如它听起来一样,我正在尝试使用IO完成的异步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。 同样对于comport句柄。
  • 一切都被传递给线程

我从代码项目看了一下CDirectoryChangeWatcher ,但是除了使用C ++和更多的线程之外,我看不出我在做什么不同。 如果我错过了一些东西,请随意指出!

如果有帮助,输出基本上是重复的,无论我改变了多少目录。

 GetQueuedCompletionStatus(): Failed, Error Code 258 

我意识到发布代码墙通常被认为是可怕的,但这是我如何工作:

新结构:

 BOOL runthread; typedef struct overlapped_struct { OVERLAPPED overlapped; wchar_t* buffer; } overlapped_t; typedef struct dirinfo_struct { HANDLE hDirOPPort; HANDLE hDirFH; overlapped_t* o; int len_buffer; wchar_t* buffer; wchar_t* directory_name; ULONG_PTR CompletionKey; } dirinfo_t; int somekey = 1; 

分配方法:

 void dirinfo_init(dirinfo_t* t) { t->buffer = malloc(16777216*sizeof(wchar_t)); t->len_buffer = 16777216; t->o = calloc(1, sizeof(overlapped_t)); t->o->buffer = calloc(16777216, sizeof(wchar_t)); memset(t->o->buffer, 0, 16777216); memset(t->o, 0, sizeof(OVERLAPPED)); } void dirinfo_free(dirinfo_t* t) { free(t->buffer); free(t->o->buffer); free(t->o); free(t); } 

main()的重要内容是这样的:

 dirinfo_t* d = malloc(1*sizeof(dirinfo_t)); d->CompletionKey = (ULONG_PTR)&somekey; dirinfo_init(d); /* set up */ runthread = TRUE; d->hDirFH = CreateFile(L"C:\\hydratest", FILE_LIST_DIRECTORY, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL); d->hDirOPPort = CreateIoCompletionPort(d->hDirFH, NULL, (ULONG_PTR)d->CompletionKey, 1); 

最后我的等待线程。 这是关键:我没有传递重叠结构。我传入的结构包含OVERLAPPED加上相当数量的基于wchar_t的存储。 由于我不完全理解的原因,这是有效的。 编辑看到这个答案 。 我相信这里的数据区域充当重叠缓冲区。

 DWORD WINAPI WaitingThread(void* args) { DWORD errorcode = 0; // an error code BOOL bResultQ = FALSE; // obvios=us BOOL bResultR = FALSE; DWORD NumBytes = 0; FILE_NOTIFY_INFORMATION* pInfo = NULL; // the data incoming is a pointer // to this struct. int i = 0; dirinfo_t* d = (dirinfo_t*) args; // rescue struct from thread arg. 

然后我们进入主线程本身。 试验和错误表明您应该同时调用ReadDirectoryW和GetQueueCompletionStatus。 我认为这意味着我们不应该从ReadDirectoryChangeW **触摸缓冲区,除非*我们告诉我们可以通过GetQueue 。 然而,对该假设的更正表示欢迎。

  while ( runthread ) { bResultR = ReadDirectoryChangesW(d->hDirFH, (void*)d->buffer, 16777216, 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, &d->o->overlapped, NULL ); bResultQ = GetQueuedCompletionStatus(d->hDirOPPort, &NumBytes, &(d->CompletionKey), (LPOVERLAPPED*)(d->o), 1000); 

所以,现在我们调用了这些函数, 然后测试它们都返回true。 如果你的参数设置正确,那么丑陋的警告 bResultR总是返回true,或者在我看来。 但是, bResultQ根据端口上是否有新数据而有所不同。

  if ( bResultQ && bResultR ) { 

所以这里我们从ReadDirectoryChangesW该缓冲区并从结构中访问信息。

  wprintf(L"\n"); pInfo = (FILE_NOTIFY_INFORMATION*) d->buffer; wprintf(L"File %s", pInfo->FileName); wprintf(L" changes %d\n", pInfo->Action); memset(d->buffer, 0, 16777216); } 

否则, 感谢Tony ,你可以放心地忽略WAIT_TIMEOUT错误,但其他任何东西都可能意味着你遇到了麻烦。

  else { errorcode = GetLastError(); if ( errorcode == WAIT_TIMEOUT ) { printf("GetQueuedCompletionStatus(): Timeout\n"); } else { printf("GetQueuedCompletionStatus(): Failed\n"); printf("Error Code %d\n", errorcode); } Sleep(500); } } return 0; } 

这完成了我认为的一个有效的例子。

一些说明:

  • 我已经将缓冲区大小设置得很大。 我注意到复制了100个文件,或者缓冲区空间设置为8192并且错过了一两个项目,这里和那里。 所以我不指望这总能拿走一切。 我的解决方案是说每100个事件,validation文件树是否是您认为使用此方法时的情况。 然而,无限更好的解决方案是不断枚举潜在的大树。

注意:要正确捕获GetQueuedCompletionStatus错误,因为很难确定此函数实际返回,应按如下方式完成:

例:

 DWORD dwNumBytes; ULONG_PTR CompletionKey; OVERLAPPED* pOverlapped; //hIOCP is initialized somewhere else in the program BOOL bOK = GetQueuedCompletionStatus(hIOCP, &dwNumBytes, &CompletionKey, &pOverlapped, 1000); DWORD dwError = GetLastError(); if(bOK) { // Process a successfully completed I/O event. } else { if (pOverlapped != NULL) { // Process a failed completed I/O request //dwError contains the reason for failure } else { if (dwError == WAIT_TIMEOUT) { //Time-out while waiting for completed I/O entry. } else { //Bad call to GetQueuedCompletionStatus //dwError contains the reason for the bad call. } } 

从本书中获取的示例(Windows via C / C ++)请尝试在代码中实现此error handling。

另外“…调用GetQueuedCompletionStatus线程以后进先出(LIFO)方式被唤醒。”

重叠结构:

执行异步设备I / O时,必须通过pOverlapped参数将地址传递给初始化的OVERLAPPED结构。 在此上下文中,“重叠”一词意味着执行I / O请求所花费的时间与线程执行其他任务所花费的时间重叠。

当你调用ReadFileWriteFile时 ,它正在讨论参数,就像上面的注释一样,这需要初始化这个结构。

它看起来如下:

 typedef struct _OVERLAPPED { ULONG_PTR Internal; ULONG_PTR InternalHigh; union { struct { DWORD Offset; DWORD OffsetHigh; }; PVOID Pointer; }; HANDLE hEvent; } OVERLAPPED, *LPOVERLAPPED; 

注意 :您正在将指向结构的指针传递给CreateIoCompletionPort函数的dwCompletionKey参数。 在参考文献中,我看到它们只是传递一个常量( #define CK_FILE 1 )。 它确实说你可以传递你喜欢的任何东西,因为操作系统并不关心。 只是想指出它。