【发布时间】:2016-11-14 16:31:36
【问题描述】:
简介:
我正在尝试在循环中异步使用ReadDirectoryChangesW 。
下面的 sn-p 说明了我想要实现的目标:
DWORD example()
{
DWORD error = 0;
OVERLAPPED ovl = { 0 };
ovl.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
if (NULL == ovl.hEvent) return ::GetLastError();
char buffer[1024];
while(1)
{
process_list_of_existing_files();
error = ::ReadDirectoryChangesW(
m_hDirectory, // I have added FILE_FLAG_OVERLAPPED in CreateFile
buffer, sizeof(buffer), FALSE,
FILE_NOTIFY_CHANGE_FILE_NAME,
NULL, &ovl, NULL);
// we have new files, append them to the list
if(error) append_new_files_to_the_list(buffer);
// just continue with the loop
else if(::GetLastError() == ERROR_IO_PENDING) continue;
// RDCW error, this is critical -> exit
else return ::GetLastError();
}
}
问题:
当ReadDirectoryChangesW返回FALSE而GetLastError()代码为ERROR_IO_PENDING时,我不知道如何处理。
在这种情况下,我应该继续循环并继续循环,直到 ReadDirectoryChangesW 返回 buffer 我可以处理。
我为解决这个问题所做的努力:
我曾尝试使用WaitForSingleObject(ovl.hEvent, 1000),但它因错误1450 ERROR_NO_SYSTEM_RESOURCES 而崩溃。以下是重现此行为的 MVCE:
#include <iostream>
#include <Windows.h>
DWORD processDirectoryChanges(const char *buffer)
{
DWORD offset = 0;
char fileName[MAX_PATH] = "";
FILE_NOTIFY_INFORMATION *fni = NULL;
do
{
fni = (FILE_NOTIFY_INFORMATION*)(&buffer[offset]);
// since we do not use UNICODE,
// we must convert fni->FileName from UNICODE to multibyte
int ret = ::WideCharToMultiByte(CP_ACP, 0, fni->FileName,
fni->FileNameLength / sizeof(WCHAR),
fileName, sizeof(fileName), NULL, NULL);
switch (fni->Action)
{
case FILE_ACTION_ADDED:
{
std::cout << fileName << std::endl;
}
break;
default:
break;
}
::memset(fileName, '\0', sizeof(fileName));
offset += fni->NextEntryOffset;
} while (fni->NextEntryOffset != 0);
return 0;
}
int main()
{
HANDLE hDir = ::CreateFile("C:\\Users\\nenad.smiljkovic\\Desktop\\test",
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
if (INVALID_HANDLE_VALUE == hDir) return ::GetLastError();
OVERLAPPED ovl = { 0 };
ovl.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
if (NULL == ovl.hEvent) return ::GetLastError();
DWORD error = 0, br;
char buffer[1024];
while (1)
{
error = ::ReadDirectoryChangesW(hDir,
buffer, sizeof(buffer), FALSE,
FILE_NOTIFY_CHANGE_FILE_NAME,
NULL, &ovl, NULL);
if (0 == error)
{
error = ::GetLastError();
if (ERROR_IO_PENDING != error)
{
::CloseHandle(ovl.hEvent);
::CloseHandle(hDir);
return error;
}
}
error = ::WaitForSingleObject(ovl.hEvent, 0);
switch (error)
{
case WAIT_TIMEOUT:
break;
case WAIT_OBJECT_0:
{
error = processDirectoryChanges(buffer);
if (error > 0)
{
::CloseHandle(ovl.hEvent);
::CloseHandle(hDir);
return error;
}
if (0 == ::ResetEvent(ovl.hEvent))
{
error = ::GetLastError();
::CloseHandle(ovl.hEvent);
::CloseHandle(hDir);
return error;
}
}
break;
default:
error = ::GetLastError();
::CloseHandle(ovl.hEvent);
::CloseHandle(hDir);
return error;
break;
}
}
return 0;
}
通读文档,我似乎需要GetOverlappedResult,最后一个参数设置为FALSE,但我不知道如何正确使用这个API。
问题:
由于 MVCE 很好地说明了我正在尝试做什么(打印新添加文件的名称),您能否告诉我必须在 while 循环中修复哪些内容才能使其正常工作?
同样,重点是在循环中异步使用ReadDirectoryChangesW,如 INTRODUCTION 中的 sn-p 所示。
【问题讨论】:
-
该代码没有多大意义。它爆炸了,因为您使用 0 超时而不是 INFINITE,因此您实际上并没有等待并以非常高的速率调用 ReadDirectoryChangesW。仅当您有其他事情要做时才使用 OVERLAPPED。所以你可以使用 WaitForMultipleObjects()。如发布的那样,您不应该使用它。
-
@HansPassant:如果 RDCW 没有返回我可以使用的缓冲区,我想
continue循环,执行其他代码(从顶部开始foo()),直到我得到填充缓冲区。那是我等了0秒的地方。如果没有可用的内容,则循环继续,否则我处理缓冲区内容。抱歉,我的母语不是英语,所以如果我的意图在第一次阅读时不清楚,我可以理解。 -
@AlwaysLearningNewStuff 如果 RDCW 返回 IO Pending 错误,则在首先发出重叠事件信号之前不要再次调用 RDCW。在此期间,您可以继续循环并做其他事情。在显示的代码中,在进入循环之前调用 RDCW 一次,然后在
WAIT_OBJECT_0处理程序中再次调用它。 -
如果你像这样在一个紧密的循环中运行它,为什么还要使用异步呢?只需使用阻塞调用。
-
@JonathanPotter:根据前面的答案的讨论,我相信他想在循环期间执行后台任务,即使 MCVE 没有显示这一点。
标签: c++ winapi asynchronous readdirectorychangesw