1

ReadDirectoryChangesWイベントが欠落し続けるという問題があります。

私は多くのグーグル検索を行い、次の関数の引数は私の検索によると正しいようですが、誰も確実に知りません. こんな感じで見始めます。

BOOL _watchRequestResult = false;
OVERLAPPED _ovl = { 0 };
_ovl.hEvent = ::CreateEventA(NULL, TRUE, FALSE, NULL);

_directoryHandle = ::CreateFileA("some path here", FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
  NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);

// This should be quite enough to fit multiple file events
static constexpr DWORD ResultDataLength = 10000;
// Byte size used for winapi calls and memcpy during move operation
static constexpr DWORD ResultDataByteSize = ResultDataLength * sizeof(FILE_NOTIFY_INFORMATION);
FILE_NOTIFY_INFORMATION _resultData[ResultDataLength] = { 0 };

_watchRequestResult = ::ReadDirectoryChangesW(
  _directoryHandle,
  (LPVOID)_resultData,
  ResultDataByteSize,
  TRUE,
  FILE_NOTIFY_CHANGE_FILE_NAME,
  NULL,
  &_ovl,
  NULL
);

_ovl.hEvent以上で、ご利用お待ちしておりWaitForMultipleObjectsます。監視スレッドに終了を指示するイベントも常にあるため、複数のオブジェクトを使用します。

ovl.hEventが通知された場合、私はこれを行います:

DWORD _ovlBytesReturned = 0;
// Imagine some struct that I use to pass the file info, not important how it looks
std::vector<MyFileInfoStruct> results;
if (::GetOverlappedResult(_directoryHandle, &_ovl, &_ovlBytesReturned, TRUE))
{
  int byteIndex = 0;
  bool previousWasRename = false;
  const int minSize = min(ResultDataLength, _ovlBytesReturned);
  while (byteIndex < minSize)
  {
    FILE_NOTIFY_INFORMATION* info = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(reinterpret_cast<char*>(&_resultData[0]) + byteIndex);
    byteIndex += info->NextEntryOffset;

    // read the stuff in the info
    results.push_back(MyFileInfoStruct::FromFileInfo(info));

    // If next entry index is 0, it means there is no next entry
    if (info->NextEntryOffset == 0)
    {
      break;
    }
  }
}
// if file is renamed, merge new name and old name to same result. However rename works to give me two FILE_NOTIFY_INFORMATION that both contain expected data
MergeResultRename(results)
// results is always 1 item long

この時点で、常に 0 であるとは限らないことに注意してください。ファイルの名前を変更すると、新しいファイル名用と古いファイル名用のinfo->NextEntryOffset2 つのエントリが正しく取得されます。_resultData

イベントごとに複数のファイルが変更されることは決してありません。これは問題です。コード全体は次のようになります (疑似コード)

Let EVENTS be an array of HANDLE event objects
Let FILE_CHANGES be a buffer of file changes 
while(shouldBeWatching) 
{
  Wait for events from previous iteration stored in EVENTS array. Skip on first iteration.
  if(event has fired) 
  {
    if(event that fired is ovl.hEvent)
    {
      Put file changes from the event that fired into FILE_CHANGES array (seen in 2nd code sample above)
      Delete and close all handles related to the event:
         Close directory handle
         Close ovl.hEvent
    }
    else 
    {
      Close everything and quit thread.
    }
  }
  Start new request (seen above in 1st code sample)
  if(FILE_CHANGES is not empty) 
  {
    Process all info from FILE_CHANGES
  }
}

これで、配列を処理するReadDirectoryChangesW にリクエストを再開したことがわかります。MyFileInfoStructしかし問題は、2 つ以上のファイルがコピーされた場合、前のファイルを処理している間に 2 番目のファイルがイベントによって登録されますが、最後の変更を「取得」してイベントを再開するまで、後続の変更は無視されます。

2 番目のスレッドで FILE_CHANGES部分からすべての情報を処理することで、これを部分的に解決できます。ただし、開始リクエスト全体を作成することで、イベントを見逃す可能性を減らすだけです->待機->ピックアップ->イベントルーチンの再起動を少し高速にします。ReadDirectoryChangesW実際には 100% のカバレッジを提供するわけではありません。リクエストが保留されていない瞬間がまだあります。

私はインターネットで多くのことを読んでいて、2つの解決策が頻繁に言及されていることを発見しました:

  • ファイルの変更を処理するために別のスレッドを使用します(私はすでにそれを行いました)
  • のサイズを大きくしFILE_NOTIFY_INFORMATION[]ます。これは私にはうまくいきません.Windowsはそこに1つのイベントしか入れません

したがって、問題は次のとおりです。呼び出して結果を「取得」するまで、ファイルの変更を取得してバッファReadDirectoryChangesWGetOverlappedResult追加し続けるにはどうすればよいですか? これは可能ですか?複数の結果を1つのバッファに入れることができた人はいますか?FILE_NOTIFY_INFORMATION[]GetOverlappedResult

4

1 に答える 1