2016-10-22 7 views
0

Windowsシステム上の任意のフォルダをの任意のアクティビティを再帰的に監視する機能をQtベースのアプリケーションに追加しています(Qtの変更QFileSystemWatcherにはありません)。 CreatFileW()でフォルダを開いた後、重複したI/Oを受け取る完了ポートを作成してから、ReadDirectoryChangesW()を使用して読み込みをキューに入れます。ReadDirectoryChangesW 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++フラグメントのすべての同じ設定で期待どおりに動作します。はCreateFileW(によって生成されたハンドルを受け入れる

上記のコードで

CreateIoCompletionPort())が、ReadDirectoryChangesW()はありません。 0を返し、GetLastError()が "ハンドルが無効です(0x6)"を返します。私は32ビットと64ビットの両方のコンパイルのもとでこれを試しました。ちょっとした違いがあった場合に備えて(私は64ビット版のPythonを使っていました)。また、指定されたディレクトリは重要ではありません。私が指定したすべてのディレクトリは同じ結果を生成します。これは設定のどこかに問題があることを示しています。

HANDLEが完了ポートを生成するために有効であることが原因かもしれませんが、ReadDirectoryChangesWを(与えるだろうCreateFileW()コール)関数胸焼けで何かはありますか?

+1

あなた 'OVERLAPPED'構造は初期化されていないことになりそうです。 –

答えて

1

CreateIoCompletionPortNULLを返します。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_portの初期値が無効であるため、CreateIoCompletionPortの後ののtd.completion_portにあります。また、エラーケースを間違って処理しています(無効なハンドルを閉じてみてください)。

+0

もちろん、レミー。私はMSDNのドキュメントでその詳細を逃した。それを指摘してくれてありがとう。 –

3

I/O完了ポートを正しく初期化しておらず、OVERLAPPED構造体をまったく初期化していません。 OVERLAPPED::hEventフィールドに無効なイベントオブジェクトハンドルが含まれているため、ReadDirectoryChangesW()が失敗しています。これは、ディレクトリハンドルではなく、エラーコードが参照している無効なハンドルです。

代わりにこれを試してみてください:

#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

レスありがとう、レミー! Benが指摘したように、Python拡張のCコードはOVERLAPPED構造体を新しいイベントで初期化しませんが、まだPythonからは動作します。しかし、私がそのコードで欠落していた点は、構造体の割り当てを自動的にゼロにすることであり、私はそれが欠けていたと思います。 ThreadData構造体でmemsetを実行するとエラーが解消され、コードは今すぐ完璧に動作します!あなたはどちらも助けてくれた。 –

関連する問題