Alw*_*uff 3 c++ winapi asynchronous readdirectorychangesw
我正在尝试在循环中异步使用ReadDirectoryChangesW。
下面的片段说明了我想要实现的目标:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
我不知道如何处理时的情况下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;
}
Run Code Online (Sandbox Code Playgroud)
通读文档,似乎我需要将最后一个参数设置为GetOverlappedResultFALSE但我不知道如何正确使用此 API。
由于 MVCE 很好地说明了我想要做什么(打印新添加文件的名称),你能告诉我必须在while循环中修复什么才能使其工作吗?
同样,重点是ReadDirectoryChangesW在循环中异步使用,如 INTRODUCTION 中的片段所示。
您的程序的基本结构看起来或多或少是正确的,您只是错误地使用了异步 I/O 调用。每当没有新文件时,事件句柄上的等待立即超时,这很好,但您随后会发出一个全新的 I/O 请求,但事实并非如此。
这就是系统资源耗尽的原因;您正在完全倾斜地发出 I/O 请求,而无需等待其中任何一个请求完成。您应该只在现有请求完成后发出新请求。
(此外,您应该调用 GetOverlappedResult 来检查 I/O 是否成功。)
所以你的循环应该看起来更像这样:
::ReadDirectoryChangesW(hDir,
buffer, sizeof(buffer), FALSE,
FILE_NOTIFY_CHANGE_FILE_NAME,
NULL, &ovl, NULL);
while (1)
{
DWORD dw;
DWORD result = ::WaitForSingleObject(ovl.hEvent, 0);
switch (result)
{
case WAIT_TIMEOUT:
processBackgroundTasks();
break;
case WAIT_OBJECT_0:
::GetOverlappedResult(hDir, &ovl, &dw, FALSE);
processDirectoryChanges(buffer);
::ResetEvent(ovl.hEvent);
::ReadDirectoryChangesW(hDir,
buffer, sizeof(buffer), FALSE,
FILE_NOTIFY_CHANGE_FILE_NAME,
NULL, &ovl, NULL);
break;
}
}
Run Code Online (Sandbox Code Playgroud)
笔记:
为简单起见,已省略错误处理;我没有做任何测试或检查您的代码是否有任何其他问题。
如果可能没有任何后台任务要执行,您应该针对这种情况进行测试,并将超时设置为 INFINITE 而不是 0,否则您将旋转。
我只想显示使其工作所需的最小更改,但是调用 WaitForSingleObject 后跟 GetOverlappedResult 是多余的;对 GetOverlappedResult 的单个调用既可以检查 I/O 是否完成,也可以检索结果(如果是)。
根据要求,修改后的版本仅使用 GetOverlappedResult 并进行最少的错误检查。我还添加了一个示例,说明您如何处理没有工作要做的情况;如果您对文件所做的任何处理确实永远运行,那么您不需要那一点。
::ResetEvent(ovl.hEvent);
if (!::ReadDirectoryChangesW(hDir,
buffer, sizeof(buffer), FALSE,
FILE_NOTIFY_CHANGE_FILE_NAME,
NULL, &ovl, NULL))
{
error = GetLastError();
if (error != ERROR_IO_PENDING) fail();
}
while (1)
{
BOOL wait;
result = process_list_of_existing_files();
if (result == MORE_WORK_PENDING)
{
wait = FALSE;
}
else if (result == NO_MORE_WORK_PENDING)
{
wait = TRUE;
}
if (!::GetOverlappedResult(hDir, &ovl, &dw, wait))
{
error = GetLastError();
if (error == ERROR_IO_INCOMPLETE) continue;
fail();
}
processDirectoryChanges(buffer);
::ResetEvent(ovl.hEvent);
if (!::ReadDirectoryChangesW(hDir,
buffer, sizeof(buffer), FALSE,
FILE_NOTIFY_CHANGE_FILE_NAME,
NULL, &ovl, NULL))
{
error = GetLastError();
if (error != ERROR_IO_PENDING) fail();
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4717 次 |
| 最近记录: |