在循环中异步使用 ReadDirectoryChangesW

2023-11-30

介绍:

我正在尝试使用读取目录更改W在循环中异步进行。

下面的代码片段说明了我想要实现的目标:

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(); 
    }
}

PROBLEM:

我不知道如何处理这个案子ReadDirectoryChangesW回报FALSE with 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;
}

仔细阅读文档,我似乎需要获取重叠结果最后一个参数设置为FALSE但我不知道如何正确使用这个API。

问题:

由于 MVCE 很好地说明了我正在尝试执行的操作(打印新添加的文件的名称),您能否向我展示在 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;
        }
    }

Notes:

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

  • 如果可能没有任何后台任务要执行,您应该测试这种情况,并在发生这种情况时将超时设置为 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();
        } 
    }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在循环中异步使用 ReadDirectoryChangesW 的相关文章

随机推荐

  • 显式调用时析构函数被调用两次

    我正在用这段代码尝试 C 中的析构函数 include
  • 使用 osmdroid-android-3.0.7 运行时无法访问资源图标

    我正在尝试启动一个简单的地图活动 使用 osmdroid android 3 0 7 库显示地图和几个标记 该代码在旧版本 1 10 下运行 我收到以下错误 02 03 15 14 30 574 E AndroidRuntime 10277
  • 如何给盒子的一部分着色

    我试图显示红色和橙色框的左侧 10 并使背景的其余部分透明 或白色 这可能吗 如果是的话请告诉我该怎么做 谢谢 box background linear gradient to right red orange border 1px bl
  • 如何从 Eclipse 项目中删除 javascript 验证?

    我在我的项目中使用 Eclipse 在搞乱我的 Eclipse 设置时 我打开了 Javascript 支持 现在 eclipse 抱怨 JQuery 库有错误并且不允许我编译该项目 有谁知道如何关闭 javascript 验证吗 我实际上
  • 用Matlab对角展开矩阵

    我有一个矩阵B我想获得一个矩阵C维度的 L k m by L n L and k是输入值 B0 B1 Bk有尺寸m by n 例如 如果我有一个矩阵B 1 1 1 1 1 1 with B0 1 1 B1 1 1 and B2 1 1 以及
  • 根据包含列名的变量从不同列中选择值[重复]

    这个问题在这里已经有答案了 我有一个像这样的 data table col1 col2 col3 new 1 4 55 col1 2 3 44 col2 3 34 35 col2 4 44 87 col3 我想填充另一列matched va
  • 如何在 SoapUI 中访问需要用户名/密码的 wsdl URL?

    如何访问 wsdl URLSOAPUI 这需要用户名 密码 http localhost 8080 ws hello1 wsdl SoapUI 4 6 4 中检索受密码保护的 WSDL 时存在错误 该错误已修复5 0 0 可以从以下地址下载
  • Hashbang 与 URI 解析

    我希望将我的网站移至完全异步文档加载 但我不想使用 请求处理方法 因为 1 我不想断开链接 2 我想要一种更灵活的方式来处理站点获取的 URI 我已经能够为我的网站构建一个轻型 MVC 允许使用通用样式的 url 例如 http ddrew
  • JavaFX 8 Z 缓冲区问题

    我的问题是 JavaFX 3D 中的 Z Buffer 它似乎没有在我的机器上按预期工作 我知道以下问题 重叠的形状 and Z顺序 不过 我确实启用了 Z 缓冲区 并且节点仍然按照它们添加到场景图中的顺序进行渲染 也许我缺少一些依赖项或其
  • 底层的鼠标事件被忽略

    我有两层 AnchorPanes 其中一层与 StackPane 堆叠在一起 所以两个图层都填充了整个场景 问题是 只有顶层接收鼠标事件 这就是场景的构建方式 只有按钮 B 接收点击事件 按钮 A 则不接收 如果我将 B 层设置为鼠标透明
  • PHP中如何连接多个三元运算符? [复制]

    这个问题在这里已经有答案了 我经常使用三元运算符 但我似乎无法将多个三元运算符堆叠在一起 我知道堆叠多个三元运算符会降低代码的可读性但在某些情况下我会 喜欢这样做 这是我到目前为止所尝试过的 foo 1 bar foo 1 1 foo 2
  • Xamarin Forms 中的工具栏项目居中对齐

    我有一个内容页面 添加了工具栏 如下所示 内容页 XamlCompilation XamlCompilationOptions Compile public partial class ChartList ContentPage publi
  • BufferedImage 用透明像素填充矩形

    我有一个 BufferedImage 我试图用透明像素填充一个矩形 问题是 透明像素并没有替换原始像素 而是放在顶部而不执行任何操作 怎样才能完全去掉原来的像素呢 该代码适用于任何其他不透明颜色 public static Buffered
  • 错误 NETSDK1032:RuntimeIdentifier 平台“ios-arm64”和 PlatformTarget“x64”必须兼容

    我在这里已经遇到了这个错误 dotnet 发布错误但我只需创建一个全新的解决方案就可以摆脱它 好吧 这不再是一个选择 因为我现在正在做一个真正的项目 经过我在这些错误中度过的许多天 我能够编译一些项目并将它们分发到 iPhone 上 只是
  • 如何在 RedHat 上的 bash 脚本中不输入密码的情况下切换到 root 用户

    我想通过在代码中指定密码而不是输入密码来在 RedHat 上的 bash 脚本中切换到 root 用户 任何帮助将不胜感激 Thanks 在脚本中以纯文本形式存储密码并不是一个好主意 您可以使用visudo编辑sudoers文件并允许用户使
  • JavaScript 在 AJAX 调用时不起作用

    我有一个页面执行 AJAX 调用并加载整个页面 加载的页面有一些 Javascript 加载时 JavaScript 可以在页面上自行工作 但是当 AJAX 加载时 Javascript 就不起作用 我不知道我错过了什么 加载页面的代码
  • 无法在 Electron Forge 中使用静态文件

    我无法在 Electron 中一致地加载图像 我正在使用 Electron Forge 和 webpack 模板https www electronforge io templates webpack template 我的 src 目录如
  • 响应按键 JavaScript

    如果这是基本的 我很抱歉 但我已经搜索并没有发现任何有效的方法 我想加载一个网页 当该页面加载时 它会显示一个图像 我想让页面自动开始监听向右箭头键的按下 当发生这种情况时 我的脚本中的一个函数将更改图像 我已经通过使用单击时做出反应的按钮
  • SqlDataReader.Read() 总是返回 false

    我有以下情况 using SqlConnection conexao new SqlConnection ConnectionString SqlCommand comando new SqlCommand query conexao co
  • 在循环中异步使用 ReadDirectoryChangesW

    介绍 我正在尝试使用读取目录更改W在循环中异步进行 下面的代码片段说明了我想要实现的目标 DWORD example DWORD error 0 OVERLAPPED ovl 0 ovl hEvent CreateEvent NULL TR