Ожидание, пока файл не станет доступным для чтения с Win32 - PullRequest
6 голосов
/ 17 ноября 2009

Я смотрю каталог, вызывая ReadDirectoryChangesW синхронно. Когда новый файл доступен, я пытаюсь получить к нему немедленный доступ с помощью CreateFile с GENERIC_READ и FILE_SHARE_READ, но это дает мне ERROR_SHARING_VIOLATION. Процесс, который помещает файл в наблюдаемый каталог, не завершает запись к тому времени, когда я пытаюсь прочитать его.

Есть ли способ надежно подождать, пока файл не станет доступным для чтения? Я могу поместить метод в цикл, подобный приведенному ниже, но я надеюсь, что есть лучший способ.

while ((hFile = CreateFile (path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE)
{
    if (GetLastError() == ERROR_SHARING_VIOLATION)
        Sleep (500);
    else
        break; // some other error occurred
}

if (hFile == INVALID_HANDLE_VALUE)
{
    // deal with other error
    return 0;
}

ReadFile (...);

Ответы [ 4 ]

6 голосов
/ 17 ноября 2009

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

int delay= 10;
while ((hFile = CreateFile (path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE)
{
    if (GetLastError() == ERROR_SHARING_VIOLATION) {
        Sleep (delay);
        if (delay<5120) // max delay approx 5.Sec
            delay*= 2;
    }
    else
        break; // some other error occurred
}
2 голосов
/ 13 октября 2014

Как сказал @Matt Davis, к сожалению, нет API пользовательского режима, но есть обходной путь, который в зависимости от вашего варианта использования (я написал мой ниже) может делать то, что вы хотите.

В прошлом мне удавалось регистрировать FILE_NOTIFY_CHANGE_LAST_WRITE вместо FILE_NOTIFY_CHANGE_FILE_NAME при звонке ReadDirectoryChangesW:

ZeroMemory(&overlapped, sizeof(OVERLAPPED));
overlapped.hEvent = hChangeEvent;

// ...    
ReadDirectoryChangesW(hSpoolPath,
                      eventBuffer,
                      EVENT_BUF_LENGTH,
                      FALSE,
                      FILE_NOTIFY_CHANGE_LAST_WRITE, // <----
                      NULL,
                      &overlapped,
                      NULL);
// ...
HANDLE events[2];

events[0] = hChangeEvent;
events[1] = hCancelEvent;

DWORD wRc = WaitForMultipleObjects(2, events, FALSE, DIRECTORY_WATCH_TIMEOUT);

Время последней записи обновляется, как только процесс-владелец закрывает дескриптор после создания файла и записи в него.

Мой вариант использования был одним процессом, который получал HTTP-запросы через TCP / IP и записывал HTTP-тело в каталог, где другой процесс подхватил его, как только процесс получения завершил запись (и, следовательно, закрыл дескриптор). ) Это. Http-сервер был единственным процессом, который писал в этот каталог, поэтому я мог положиться на шаблон create-write-close.

2 голосов
/ 17 ноября 2009

Нет API пользовательского режима для уведомлений о закрытом файле, о котором я знаю. Цикл, который вы предложили, действительно самый лучший. Единственное, что вы могли бы сделать, это наблюдать за CloseFile в драйвере фильтра, аля Process Monitor, но блин ...

0 голосов
/ 17 ноября 2009

Если вы знаете что-то о том, как создается файл, возможно, подождите, пока файл перестанет расти на Х секунд, или подождите, пока файл дозорного не будет удален. Или ощутите состояние программы, которая их создает.

...