gpt4 book ai didi

c++ - 在循环中异步使用 ReadDirectoryChangesW

转载 作者:行者123 更新时间:2023-11-30 00:45:59 26 4
gpt4 key购买 nike

简介:

我正在尝试使用 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();
}
}

问题:

我不知道如何处理 ReadDirectoryChangesW 返回 FALSEGetLastError() 代码为 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,如简介中的片段所示。

最佳答案

您的程序的基本结构看起来或多或少没问题,您只是错误地使用了异步 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;
}
}

注意事项:

  • 为简单起见,省略了错误处理;我没有进行任何测试或检查您的代码是否存在任何其他问题。

  • 如果可能没有任何后台任务要执行,您应该针对这种情况进行测试,并在它发生时将超时设置为 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();
}
}

关于c++ - 在循环中异步使用 ReadDirectoryChangesW,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40593500/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com