10

おっしゃる通り、ReadDirectoryChangesWIO Completion で非同期を試みていますが、うまくいきません。具体的には、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 ではありません。コンポートハンドルも同様です。
  • すべてがスレッドに渡されます

私はコード プロジェクトからCDirectoryChangeWatcherを見てきましたが、C++ の使用とその他の多くのスレッドは別として、自分が何をしているのかわかりません。私が何かを見逃している場合は、遠慮なく指摘してください!

問題のディレクトリをどれだけ変更しても、出力が役立つ場合は、基本的に繰り返されます。

GetQueuedCompletionStatus(): Failed, Error Code 258
4

2 に答える 2

7

コードの壁を投稿することは一般的に恐ろしいと考えられていますが、これを機能させる方法は次のとおりです。

新しい構造体:

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ベース ストレージを含む構造体を渡します。私が完全に理解していない理由で、これはうまくいきます。編集この回答を参照してください。ここでのデータ領域はオーバーラップ バッファとして機能すると思います。

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);
        }

それ以外の場合は、Tony のおかげで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 個のファイルをコピーしていて、バッファーが設定されたスペースを使い果たし、81921 つまたは 2 つの項目をあちこちで見逃していることに気付きました。したがって、これが常にすべてをピックアップするとは思いません。私の解決策は、100イベントごとに、この方法を使用した場合にファイルツリーがあなたが考えているものであることを確認することです。ただし、潜在的に大きなツリーを常に列挙するための無限に優れたソリューションです。
于 2011-05-27T22:54:18.407 に答える
1

注:GetQueuedCompletionStatusこの関数が実際に返されたかどうかを判断するのは難しいため、適切にエラーをキャッチするには、次のように実行する必要があります。

例:

DWORD dwNumBytes;
ULONG_PTR CompletionKey;
OVERLAPPED* pOverlapped;

//hIOCP is initialized somewhere else in the program
BOOL bOK = GetQueuedCompletionStatus(hIOCP, &dwNumBytes, &CompletionKey, &pOverlapped, 1000);

DWORD dwError = GetLastError();

if(bOK)
{
// Process a successfully completed I/O event.
}
else
{
  if (pOverlapped != NULL)
  {
    // Process a failed completed I/O request
    //dwError contains the reason for failure
  }
  else {
      if (dwError == WAIT_TIMEOUT)
      {
         //Time-out while waiting for completed I/O entry.
      }
      else {
          //Bad call to GetQueuedCompletionStatus
          //dwError contains the reason for the bad call.
      }
}

本からの例 (C/C++ 経由の Windows) コードにこのエラー処理を実装してみてください。

また、「... 呼び出すスレッドはGetQueuedCompletionStatus、後入れ先出し (LIFO) 方式で起こされます。」

オーバーラップ構造:

非同期デバイス I/O を実行するときは、 pOverlappedパラメータを介して初期化済みのOVERLAPPED構造体にアドレスを渡す必要があります。このコンテキストでの「オーバーラップ」という言葉は、I/O 要求の実行に費やされた時間が、スレッドが他のタスクの実行に費やされた時間と重複していることを意味します。

上記の注意として、ReadFileまたはWriteFileを呼び出すときのパラメーターについて話しているため、この構造を初期化する必要があります。

次のようになります。

typedef struct _OVERLAPPED {
  ULONG_PTR Internal;
  ULONG_PTR InternalHigh;
  union {
    struct {
      DWORD Offset;
      DWORD OffsetHigh;
    };
    PVOID  Pointer;
  };
  HANDLE    hEvent;
} OVERLAPPED, *LPOVERLAPPED;

:構造体へのポインタを関数のdwCompletionKeyパラメータに渡していますCreateIoCompletionPort。私が見ているリファレンスでは、#define CK_FILE 1これに定数 ( ) を渡すだけです。OSは気にしないので、好きなものを渡すことができると言っています。ただし、それを指摘したかっただけです。

于 2011-05-27T19:37:36.583 に答える