听起来确实如此,我正在尝试使用IO Completion进行异步ReadDirectoryChangesW,但它不起作用,特别是,GetLastError反复返回258(GetQueuedCompletionStatus超时)。

我有结构:

typedef struct dirinfo_struct
{
    HANDLE hDirFH;           // directory handle
    OVERLAPPED Overlapped;   // overlapped storage
    int len_buffer;          // buffer length
    wchar_t* buffer;         // buffer itself
    wchar_t* directory_name; // target name
} dirinfo_t;

typedef struct dirmon_struct
{
    HANDLE hDirOPPort;       // handle to the IO port.
    dirinfo_t* dirinfo;      // pointer to the struct above.
} dirmon_t;

用于存储相关信息。这是初始化的:
dirinfo_t* t = malloc(1*sizeof(dirinfo_t));
dirmon_t* d = malloc(1*sizeof(dirmon_t));
dirinfo_init(t); // does t->buffer = malloc(8192*sizeof(wchar_t));

然后创建目录句柄和com端口:
t->hDirFH = CreateFile(L"C:\\test",
                        FILE_LIST_DIRECTORY,
                        FILE_SHARE_READ|FILE_SHARE_WRITE,
                        NULL,
                        OPEN_EXISTING,
                        FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
                        NULL);
d->dirinfo = t;
d->hDirOPPort = CreateIoCompletionPort(d->dirinfo->hDirFH,
                                       NULL,
                                       (ULONG_PTR)(d->dirinfo),
                                       1);

然后,我通过d将此信息传递给新线程。现在说新线程我有:
bResultQ = GetQueuedCompletionStatus(d->hDirOPPort, lpBytes,
                                     (ULONG_PTR*)d->dirinfo,
                                     lpOverlapped, 1000);

if ( bResultQ )
{
    bResultR = ReadDirectoryChangesW(d->dirinfo->hDirFH,
                                     (void*)d->dirinfo->buffer,
                                     8192, TRUE,
                                     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,
                                     lpReadDirBytes,
                                     &d->dirinfo->Overlapped,
                                     NULL );
}
else
{
    printf("GetQueuedCompletionStatus(): Failed, ");
    errorcode = GetLastError();
    printf("Error Code %d\n", errorcode);
    Sleep(500);
}

因此,我将其设置为关闭运行,由于目录未更改,因此我应该获得超时(258错误)。但是,即使更改目录,我仍然会收到错误消息。换句话说,这些更改不会被接受。这使我相信我的设置不正确。

有任何想法吗?

注意事项:
  • 具有讽刺意味的是,最终它将通过pywin32转换为Python。但是,直到我了解这在C语言中应该如何工作之前,我都不会去那里。
  • 不能选择同步ReadDirectoryChangesW。如果未触发任何事件,则我需要该线程处于超时状态,以便它可以检查它是否仍在运行。
  • 我专门用C而不是C++编写。
  • FindFirstChangeNotification等也不是一个选项-我不想继续比较目录列表以找出已更改的内容。

  • 其他说明:
  • 目录存在,该句柄不为NULL。对于comport句柄也是如此。
  • 一切都传递给了
  • 线程

    我从代码项目看过CDirectoryChangeWatcher,但是除了使用C++和更多线程外,我看不出我在做什么。如果我错过了什么,请随时指出!

    如果有帮助,则输出基本上是重复的,无论我更改有问题的目录有多少。
    GetQueuedCompletionStatus(): Failed, Error Code 258
    

    最佳答案

    我意识到发布代码墙通常被认为是可怕的,但是这是我如何做到这一点的方法:

    新结构:

    BOOL runthread;
    
    typedef struct overlapped_struct
    {
        OVERLAPPED overlapped;
        wchar_t* buffer;
    } overlapped_t;
    
    typedef struct dirinfo_struct
    {
    
        HANDLE hDirOPPort;
        HANDLE hDirFH;
        overlapped_t* o;
        int len_buffer;
        wchar_t* buffer;
        wchar_t* directory_name;
        ULONG_PTR CompletionKey;
    } dirinfo_t;
    
    int somekey = 1;
    

    分配方法:
    void dirinfo_init(dirinfo_t* t)
    {
        t->buffer = malloc(16777216*sizeof(wchar_t));
        t->len_buffer = 16777216;
        t->o = calloc(1, sizeof(overlapped_t));
        t->o->buffer = calloc(16777216, sizeof(wchar_t));
        memset(t->o->buffer, 0, 16777216);
        memset(t->o, 0, sizeof(OVERLAPPED));
    }
    
    void dirinfo_free(dirinfo_t* t)
    {
        free(t->buffer);
        free(t->o->buffer);
        free(t->o);
        free(t);
    }
    
    main()的重要内容可以做到这一点:
    dirinfo_t* d = malloc(1*sizeof(dirinfo_t));
    d->CompletionKey = (ULONG_PTR)&somekey;
    dirinfo_init(d);
    
    /* set up */
    runthread = TRUE;
    d->hDirFH = CreateFile(L"C:\\hydratest",
                    FILE_LIST_DIRECTORY,
                    FILE_SHARE_READ|FILE_SHARE_WRITE,
                    NULL,
                    OPEN_EXISTING,
                    FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
                    NULL);
    
    d->hDirOPPort = CreateIoCompletionPort(d->hDirFH, NULL,
                          (ULONG_PTR)d->CompletionKey, 1);
    

    然后最后是我的等待线程。这是关键:我没有传入重叠的结构。我传入的是包含OVERLAPPED加上大量基于wchar_t的存储的结构。由于某些原因,我无法完全理解,这是可行的。 编辑参见this answer。我相信这里的数据区域充当了重叠缓冲区。
    DWORD WINAPI WaitingThread(void* args)
    {
        DWORD errorcode = 0;    // an error code
        BOOL bResultQ = FALSE;  // obvios=us
        BOOL bResultR = FALSE;
        DWORD NumBytes = 0;
        FILE_NOTIFY_INFORMATION* pInfo = NULL; // the data incoming is a pointer
                                               // to this struct.
        int i = 0;
        dirinfo_t* d = (dirinfo_t*) args;      // rescue struct from thread arg.
    

    然后我们进入主线程本身。反复试验提示您应该同时调用ReadDirectoryW和GetQueueCompletionStatus。 我认为的意思是我们应该不要触摸ReadDirectoryChangeW的缓冲区,除非GetQueue告诉我们可以。但是,欢迎对该假设进行更正。
        while ( runthread )
        {
            bResultR = ReadDirectoryChangesW(d->hDirFH, (void*)d->buffer,
                                              16777216, TRUE,
                   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,
                                              NULL,
                                              &d->o->overlapped,
                                              NULL );
            bResultQ = GetQueuedCompletionStatus(d->hDirOPPort,
                                                 &NumBytes, &(d->CompletionKey),
                                                 (LPOVERLAPPED*)(d->o), 1000);
    

    因此,现在我们调用了这些函数,然后测试它们是否都返回true。 丑陋的警告,如果正确设置了参数bResultR始终返回true,或者在我看来。但是,bResultQ取决于端口上是否有新数据而有所不同。
            if ( bResultQ && bResultR )
            {
    

    因此,在这里,我们从ReadDirectoryChangesW转换该缓冲区,并从结构访问信息。
                wprintf(L"\n");
                pInfo = (FILE_NOTIFY_INFORMATION*) d->buffer;
                wprintf(L"File %s", pInfo->FileName);
                wprintf(L" changes %d\n", pInfo->Action);
                memset(d->buffer, 0, 16777216);
            }
    

    否则,使用thanks to Tony for this,您可以放心地忽略WAIT_TIMEOUT错误,但是其他任何可能都意味着您有麻烦。
            else
            {
                errorcode = GetLastError();
    
                if ( errorcode == WAIT_TIMEOUT )
                {
                    printf("GetQueuedCompletionStatus(): Timeout\n");
                }
                else
                {
                    printf("GetQueuedCompletionStatus(): Failed\n");
                    printf("Error Code %d\n", errorcode);
                }
                Sleep(500);
            }
        }
    
        return 0;
    }
    

    这就完成了我认为是可行的示例。

    一些注意事项:
  • 我将缓冲区大小设置为很大。我注意到复制了约100个文件,因此缓冲区用尽了设置为8192的空间,并在这里和那里错过了一两个项目。因此,我不希望这总能拿走所有东西。我的解决方案是说每100个事件,如果使用此方法,请验证文件树是否符合您的想法。但是,要不断枚举可能较大的树,这是无限更好的解决方案。
  • 09-25 15:56