С++ WinApi: ReadDirectoryChangesW() Получение двойных уведомлений - программирование
Подтвердить что ты не робот

С++ WinApi: ReadDirectoryChangesW() Получение двойных уведомлений

Я пытаюсь понять функцию ReadDirectoryChangesW, чтобы я мог быть проинформирован об изменении содержимого в нескольких каталогах (файлы перезаписаны, удалены файлы, переименованы и т.д.).

Одно из моих недавних наблюдений заключается в том, что для каждого файла операции записи я получаю всегда два уведомления для одиночного файла.

Я очень тщательно проследил это, и я уверен, что если я перезапишу файл (скажем, файл .txt с новым контентом - в основном пара дополнительных букв внутри), ReadDirectoryChangesW() уведомляет меня два раза за этот файл save,

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

Известно ли это поведение? Есть ли способ получить только одно уведомление за одно изменение, пожалуйста? Есть ли способ эффективно избежать двойных уведомлений?

Я использую:

  • Неуправляемый С++
  • Visual Studio 2012
  • Windows 7 x64

Я использую довольно простой код для выполнения своих тестов, но вы захотите его увидеть, так вот:

HANDLE hDir = CreateFile(
    lpDir,
    FILE_LIST_DIRECTORY,
    FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
    NULL, 
    OPEN_EXISTING, 
    FILE_FLAG_BACKUP_SEMANTICS, 
    NULL);

    int nCounter = 0;
    FILE_NOTIFY_INFORMATION strFileNotifyInfo[1024];
    DWORD dwBytesReturned = 0;   

    while(TRUE)
    {
        if( ReadDirectoryChangesW ( hDir, (LPVOID)&strFileNotifyInfo, sizeof(strFileNotifyInfo), FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE, &dwBytesReturned, NULL, NULL) == 0)
        {
            ErrorCheck(_T("Reading Directory Change"));
        }
        else
        {
            _tcout << _T("File Modified: ") << strFileNotifyInfo[0].FileName << endl;
            _tcout << _T("Loop: ") << nCounter++ << endl;
        }
    }
4b9b3361

Ответ 1

ReadDirectoryChangesW() имеет очень миопическое представление файловой системы. Он видит все изменения в файловой системе и добросовестно сообщает об этом. И да, когда вы пишете файл, часто бывает больше одного. Это деталь реализации конкретной файловой системы, которую вы используете, но любой общий в Windows также сохраняет запись в каталоге для файла, в котором хранятся метаданные для файла.

Итак, вы видите запись для данных файла. Но вы также видите, что он меняет запись в каталоге. В частности, размер файла, который может измениться при записи файла и добавлении данных в файл. И отметки времени последней записи и последнего доступа, записанные в записи каталога. Апи в противном случае не следит за тем, как делается изменение, он видит только запись низкого уровня. Он также совершенно не осознает, какой конкретный процесс просил написать.

Это то, с чем вам придется иметь дело, нет способа отличить эти записи. Все, что вы знаете, это "файл был изменен". Как, почему, кем и как часто совершенно невозможно обнаружить.

Что-то еще, с чем вам придется иметь дело, заключается в том, что во время создания уведомления процесс, который записывает файл, скорее всего, все еще будет блокировать файл. Это мешает вам делать что-либо полезное с самим файлом. Подобно чтению файла или копированию, он, скорее всего, терпит неудачу. Вам нужно подождать, пока процесс завершится с файлом, и он закрыл свой дескриптор файла. Невозможно обнаружить это, кроме как пытаться открыть файл самостоятельно и отрицать любой обмен. Для этого требуется таймер, периодически пытающийся самостоятельно получить блокировку файла. Как только вы получите эту сантехнику, получение более одного уведомления об изменении файла не имеет значения.

Ответ 2

Это результат работы текущего кода одновременно с протоколом procmon после сохранения файла в блокноте. Есть два уведомления из ReadDirectoryChangesW() и два уведомления из procmon. 2 IRP_MJ_WRITE 1 из Блокнота (WriteFile) 1 из диспетчера системного кэша (CcWriteBehind) procmon