【问题标题】:ReadDirectoryChangesW rejecting HANDLE accepted by CreateIoCompletionPortReadDirectoryChangesW 拒绝 CreateIoCompletionPort 接受的 HANDLE
【发布时间】:2016-10-23 05:06:55
【问题描述】:

我正在向我的(基于 Qt 的)应用程序添加功能,以递归地监视我的 Windows 系统上的任意文件夹的 any 活动(Qt 变体 QFileSystemWatcher 缺少的东西)。使用 CreatFileW() 打开文件夹后,我创建了一个完成端口来接收重叠的 I/O,然后我使用 ReadDirectoryChangesW() 将读取排队。

我已将所有这些放在以下“简单”Win32 控制台应用程序中进行演示(请注意,“stdafx.h”标头已修改为包含“windows.h”,但与 Visual Studio 2013 IDE 不同生成它):

#include "stdafx.h"

#define MAX_BUFFER 4096

struct ThreadData
{;
    DWORD           winerr;
    HANDLE          handle;
    unsigned int    flags;
    int             recursive;
    HANDLE          completion_port;
    CHAR            buffer[MAX_BUFFER];
    DWORD           buffer_len;
    OVERLAPPED      overlapped;
};

int _tmain(int argc, _TCHAR* argv[])
{
    DWORD winerr;
    ThreadData td;

    td.flags = 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;
    td.recursive = 1;
    td.completion_port = INVALID_HANDLE_VALUE;
    td.handle = INVALID_HANDLE_VALUE;

    td.handle = CreateFileW(L"J:\\Font",         // arbitrary folder
                            FILE_LIST_DIRECTORY, // required
                            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                            NULL,
                            OPEN_EXISTING,
                            // Use FILE_FLAG_OVERLAPPED for asynchronous operation with ReadDirectoryChangesW.
                            FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
                            NULL);

    if(td.handle == INVALID_HANDLE_VALUE)
    {
        winerr = GetLastError();
        CloseHandle(td->handle);
        return 0;
    }

    td.completion_port = CreateIoCompletionPort(td.handle,
                                                td.completion_port,
                                                (ULONG_PTR)td,
                                                0);    // max num processors
    if(td.completion_port == INVALID_HANDLE_VALUE)
    {
        winerr = GetLastError();
        CloseHandle(td.completion_port);
        CloseHandle(td.handle);
        return 0;
    }

    BOOL rdc = ReadDirectoryChangesW(td.handle,
                                     td.buffer,    // read results
                                     MAX_BUFFER,
                                     td.recursive, // watch subdirectories
                                     // NOTE: At least one flag is required!
                                     td.flags,     // see Notify Filters below
                                     &td.buffer_len,
                                     &td.overlapped,
                                     NULL);         // completion routine
    if(rdc == 0)
    {
        winerr = GetLastError();     // "The handle is invalid. (0x6)"
        CloseHandle(td.completion_port);
        CloseHandle(td.handle);
        return 0;
    }

    // Launch thread here to handle completions and trigger new ones
    ...

    // Clean up when the thread is done
    CloseHandle(td.completion_port);
    CloseHandle(td.handle);

    return 0;
}

关于这段代码需要注意的是,它是在一个 Python 模块(“watcher”)之后建模的,用 C 语言编写,提供与 Python 环境类似的功能。我已经在 Python 中使用过它,它在这个 C++ 片段中的所有相同设置下都能正常工作。

在上述代码中,CreateIoCompletionPort() 接受 CreateFileW() 生成的 HANDLE,但 ReadDirectoryChangesW() 不接受。它返回 0,GetLastError() 返回“句柄无效。(0x6)”。我已经在 32 位和 64 位编译下都试过了,以防万一(我使用的是 64 位版本的 Python)。此外,指定的目录似乎无关紧要:我指定的所有目录都会产生相同的结果,这表明某处的设置有问题。

CreateFileW() 调用中是否有某些内容可能导致 HANDLE 对生成完成端口有效,但会给 ReadDirectoryChangesW() 函数带来胃痛?

【问题讨论】:

  • 您的 OVERLAPPED 结构看起来未初始化。

标签: c++ winapi


【解决方案1】:

您没有正确初始化 I/O 完成端口,并且您根本没有初始化 OVERLAPPED 结构。 ReadDirectoryChangesW() 失败,因为 OVERLAPPED::hEvent 字段包含无效的事件对象句柄。那是错误代码所指的无效句柄,而不是目录句柄。

试试这个:

#include "stdafx.h"

#define MAX_BUFFER 4096

struct ThreadData
{
    DWORD           winerr;
    HANDLE          handle;
    DWORD           flags;
    BOOL            recursive;
    HANDLE          completion_port;
    CHAR            buffer[MAX_BUFFER];
    DWORD           buffer_len;
    OVERLAPPED      overlapped;
};

int _tmain(int argc, _TCHAR* argv[])
{
    DWORD winerr;
    ThreadData td;

    td.flags = 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;
    td.recursive = TRUE;
    td.completion_port = NULL;
    td.handle = CreateFileW(L"J:\\Font",         // arbitrary folder
                            FILE_LIST_DIRECTORY, // required
                            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                            NULL,
                            OPEN_EXISTING,
                            // Use FILE_FLAG_OVERLAPPED for asynchronous operation with ReadDirectoryChangesW.
                            FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
                            NULL);

    if(td.handle == INVALID_HANDLE_VALUE)
    {
        winerr = GetLastError();
        return 0;
    }

    td.completion_port = CreateIoCompletionPort(td.handle,
                                                NULL,
                                                (ULONG_PTR)&td,
                                                0);    // max num processors
    if(td.completion_port == NULL)
    {
        winerr = GetLastError();
        CloseHandle(td.handle);
        return 0;
    }

    ZeroMemory(&td.overlapped, sizeof(td.overlapped)); // <-- add this!

    // required if the thread uses GetOverlappedResult()...
    // optional if the thread uses GetQueuedCompletionStatus()...
    /*
    td.overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if(td.overlapped.hEvent == NULL)
    {
        winerr = GetLastError();
        CloseHandle(td.completion_port);
        CloseHandle(td.handle);
        return 0;
    }
    */

    BOOL rdc = ReadDirectoryChangesW(td.handle,
                                     td.buffer,    // read results
                                     MAX_BUFFER,
                                     td.recursive, // watch subdirectories
                                     // NOTE: At least one flag is required!
                                     td.flags,     // see Notify Filters below
                                     &td.buffer_len,
                                     &td.overlapped,
                                     NULL);         // completion routine
    if(rdc == FALSE)
    {
        winerr = GetLastError();
        //CloseHandle(td.overlapped.hEvent);
        CloseHandle(td.completion_port);
        CloseHandle(td.handle);
        return 0;
    }

    // Launch thread here to handle completions and trigger new ones
    ...

    // Clean up when the thread is done
    //CloseHandle(td.overlapped.hEvent);
    CloseHandle(td.completion_port);
    CloseHandle(td.handle);

    return 0;
}

【讨论】:

  • 其实不需要提供事件对象。文档说“在将此结构传递给任何重叠函数之前,用户必须使用CreateEvent 函数将此成员初始化为零或有效的事件句柄。”所以错误不是因为缺少有效的事件句柄,而是因为传递了一个无效的(未初始化的)事件句柄。
  • 感谢您的回复,雷米!与 Ben 刚刚指出的一致,Python 扩展的 C 代码也没有使用新事件初始化 OVERLAPPED 结构,但它仍然可以在 Python 中工作。但是,我在该代码中遗漏的一点是结构的分配会自动将其归零,我想我错过了。对 ThreadData 结构执行 memset 清除了错误,代码现在可以完美运行!求你们俩帮忙。
【解决方案2】:

CreateIoCompletionPort 出错时返回 NULL,而不是 INVALID_HANDLE_VALUE。所以你的第一个错误就在这一行:

 td.completion_port = INVALID_HANDLE_VALUE;

应该是这样的:

 td.completion_port = NULL;

还有这个不正确的检查:

if(td.completion_port == INVALID_HANDLE_VALUE)

必须改为:

if(td.completion_port == NULL)

td.completion_portCreateIoCompletionPort 之后得到NULL,因为td.completion_port 的初始值无效。此外,您错误地处理了错误情况(比如尝试关闭无效句柄)。

【讨论】:

  • 糟糕。绝对的,雷米。我错过了 MSDN 文档中的那个细节。感谢您指出。
猜你喜欢
  • 2011-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-31
  • 2018-06-18
  • 1970-01-01
相关资源
最近更新 更多