我正在向基于Qt的应用程序中添加功能,以便以递归方式监视Windows系统上的任意文件夹的任何活动(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;
}


关于此代码的注意事项是,它是以C语言编写的Python模块(“观察程序”)为模型的,该模块提供与Python环境类似的功能。我已经在Python中使用了它,并且可以在此C ++片段中的所有相同设置下按预期工作。

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

在CreateFileW()调用中是否有可能导致HANDLE对生成补全端口有效的东西,但会给ReadDirectoryChangesW()函数带来灼热感?

最佳答案

您没有正确初始化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;
}

10-08 19:19