При вызове ReadDirectoryChangesW только первый вызов возвращает любые изменения (как синхронизированные, так и асинхронные). - PullRequest
6 голосов
/ 28 июля 2011

Ниже приведена минимальная программа, которая использует ReadDirectoryChangesW.У меня проблема в том, что возвращается только первый вызов GetQueuedCompletionStatus.Во второй раз через цикл он навсегда блокируется независимо от того, сколько изменений было внесено в каталог.

Я также пытался использовать синхронную версию и у меня точно такая же проблема.

#include <array>
#include <cassert>
#include <iostream>
#include <Windows.h>

int main() {
  // Open the directory to monitor.
  HANDLE dir = ::CreateFileA(
      "G:\\Program Files (x86)\\Steam\\steamapps\\common\\eve online"
    , FILE_LIST_DIRECTORY
    , FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE
    , NULL
    , OPEN_EXISTING
    , FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED
    , NULL
    );

  if (dir == INVALID_HANDLE_VALUE) {
    std::cout << "Failed to open directory for change notifications!\n";
    return 1;
  }

  // Setup IOCP.
  HANDLE iocp = ::CreateIoCompletionPort(
      dir
    , NULL
    , NULL
    , 1
    );

  // Monitor.
  while (true) {
    std::array<char, 1024 * 8> buf;
    DWORD bytes_read;
    OVERLAPPED overlapped;
    std::memset(&overlapped, 0, sizeof(overlapped));
    BOOL result = ::ReadDirectoryChangesW(
        dir
      , &buf.front()
      , buf.size()
      , false
      , FILE_NOTIFY_CHANGE_FILE_NAME // Includes file creation.
      , &bytes_read
      , &overlapped
      , NULL
      );

    if (result == FALSE) {
      DWORD error = ::GetLastError();
      std::cout << "Call to ReadDirectoryChangesW failed! " << error << "\n";
      return 1;
    }

    // Wait for completion.
    ULONG_PTR key;
    LPOVERLAPPED overlapped_result;

    result = ::GetQueuedCompletionStatus(
        iocp
      , &bytes_read
      , &key
      , &overlapped_result
      , INFINITE
      );

    if (result == FALSE) {
      std::cout << "Call to GetQueuedCompletionStatus failed!\n";
      return 1;
    }

    // Print results!
    for (FILE_NOTIFY_INFORMATION *fni =
           reinterpret_cast<FILE_NOTIFY_INFORMATION *>(&buf.front());
         ;
         fni = reinterpret_cast<FILE_NOTIFY_INFORMATION *>(
           reinterpret_cast<char *>(fni) + fni->NextEntryOffset)) {
      std::wstring filename(fni->FileName, fni->FileName + fni->FileNameLength);
      std::wcout << "Got change: " << filename.c_str() << "\n";

      if (fni->NextEntryOffset == 0) break;
    }
  }

}

Ответы [ 3 ]

5 голосов
/ 28 июля 2011

Несколько проблем.

Сначала вы пытаетесь вывести многобайтовые строковые литералы в wcout. Вы должны превратить их в широкие струны, добавив L.

Во-вторых, переменная FileNameLength представляет длину имени в байтах, а не в символах. Вы должны разделить его на 2, чтобы получить количество символов.

0 голосов
/ 28 июля 2011

Проблема в том, что ваша логика печати вызывает переполнение буфера, потому что fni-> FileNameLength в байтах, а не в символах. Случайное повреждение памяти объясняет, почему я получил результаты, отличные от вас.

Исправление просто так:

std :: wstring filename (fni-> FileName, fni-> FileName + fni-> FileNameLength / sizoeof (fni-> FileName [0]));

0 голосов
/ 28 июля 2011

Как вы это компилируете?При использовании Visual Studio не удается скомпилировать, поскольку третий параметр GetQueuedCompletionStatus введен неправильно.Параметр должен быть указателем на указатель на ULONG, а не указателем на ULONG.Когда я изменил объявление переменной «ключ» на

ключ ULONG_PTR;

, программы работают правильно.

...