2016-10-22 6 views
0

Я добавляю функциональность к моему (Qt-основанному) приложению для мониторинга произвольной папки в моей системе Windows для любой операции рекурсивно (что-то не имеет Qt-вариант QFileSystemWatcher). После открытия папки с помощью CreatFileW(), я создаю порт завершения для получения перекрытого ввода-вывода, а затем я запускаю очередь с использованием ReadDirectoryChangesW().ReadDirectoryChangesW отклонение HANDLE принято CreateIoCompletionPort

Я разместил все это в следующем «простом» консольном приложении 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 («наблюдател»), написанный в C, что обеспечивает функциональность, аналогичную среду Python. Я использовал его в Python, и он работает как ожидалось со всеми теми же настройками в этом фрагменте C++.

В приведенном выше коде, CreateIoCompletionPort() принимает HANDLE, генерируемый CreateFileW(), но ReadDirectoryChangesW() не делает. Он возвращает 0, и GetLastError() возвращает «Ручка недействительна. (0x6)». Я пробовал это под 32- и 64-разрядными компиляторами, на всякий случай это имело какое-либо значение (я использовал 64-битную версию Python). Кроме того, указанный каталог не имеет значения: все указанные мной каталоги приводят к одному и тому же результату, что говорит о том, что проблема связана с настройками где-то.

Есть ли что-то в CreateFileW() вызова, который может привести к HANDLE быть действительным для создания порта завершения, но даст ReadDirectoryChangesW() функцию изжоги?

+1

Ваш 'OVERLAPPED' структура выглядит быть инициализирован. –

ответ

1

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) 

Вы получаете NULL в td.completion_port после CreateIoCompletionPort, поскольку начальное значение td.completion_port недействительно. Кроме того, вы неправильно обрабатываете ошибку (например, попробуйте закрыть недействительные дескрипторы).

+0

Упс. Абсолютно, Реми. Я пропустил эту деталь в документах MSDN. Спасибо, что указали это. –

3

Вы не инициализируете порт завершения ввода-вывода правильно, и вы не инициализируете структуру 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; 
} 
+2

Фактически, предоставление объекта события не требуется. Документация гласит: «Пользователь должен инициализировать этот элемент либо нулем, либо действительным дескриптором события, используя функцию« CreateEvent », прежде чем передавать эту структуру на любые перекрывающиеся функции». Таким образом, ошибка заключается не в отсутствии действительного дескриптора события, а при передаче недействительного (неинициализированного) дескриптора события. –

+0

Спасибо за ответ, Реми! В соответствии с тем, что только что указал Бен, код C для расширения Python не инициализирует структуру OVERLAPPED новым событием, и все же он все еще работает с Python. Тем не менее, точка, которую я пропустил в этом коде, - это распределение для структуры, которое автоматически нулирует ее, что, я думаю, я отсутствовал. Выполнение memset в структуре ThreadData очистило ошибку, и теперь код работает отлично! Это вы оба за помощь. –

Смежные вопросы