C ++ / Win32: как дождаться завершения отложенного удаления

Решено:

Проблема:

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

(Т.е. во время создания файла создается дескриптор, который используется для записи, а затем закрывается. Во время обработки файла отдельный дескриптор файла открывает файл, читает из него и закрывается в EOF. И , наконец, , delete использует :: DeleteFile, который использует только имя файла, а не дескриптор файла).

Недавно мы пришли к выводу, что конкретный макрос (скрипт) иногда не может иметь возможность создать файл в любое случайное время в следующий раз (то есть он успешен в течение первых сотен итераций «создать, обработать, удалить», но когда он возвращается к созданию его сто первый раз, Windows отвечает «Доступ запрещен») .

Заглянув глубже в проблему, я написал очень простую программу, которая перебирает что-то вроде этого:

while (true) {
    HANDLE hFile = CreateFileA(pszFilename, FILE_ALL_ACCESS, FILE_SHARE_READ,
                               NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
        return OpenFailed;

    const DWORD dwWrite = strlen(pszFilename);
    DWORD dwWritten;

    if (!WriteFile(hFile, pszFilename, dwWrite, &dwWritten, NULL) || dwWritten != dwWrite)
        return WriteFailed;

    if (!CloseHandle(hFile))
        return CloseFailed;

    if (!DeleteFileA(pszFilename))
        return DeleteFailed;
}

Как видите, это напрямую связано с Win32 API и чертовски просто. Я создаю файл, записываю в него, закрываю дескриптор, удаляю, промываю, повторяю ...

Но где-то по ходу строки я ' я получу ошибку Доступ запрещен (5) во время вызова CreateFile (). Глядя на ProcessMonitor sysinternal, я вижу, что основная проблема заключается в том, что файл ожидает удаления, пока я пытаюсь создать его снова.

Вопросы:

  • Есть ли способ дождаться, пока удаление завершится. Complete?
  • Есть ли способ определить, что файл ожидает удаления?

Мы попробовали первый вариант, просто WaitForSingleObject () в HFILE. Но HFILE всегда закрывается перед выполнением WaitForSingleObject, поэтому WaitForSingleObject всегда возвращает WAIT_FAILED. Ясно, что попытка дождаться закрытого дескриптора не сработает.

Я мог бы дождаться уведомления об изменении для папки, в которой существует файл. Однако это похоже на чрезвычайно накладные расходы на то, что является проблемой только иногда (а именно: в моих тестах на моем ПК с Windows 7 x64 E6600 он обычно терпит неудачу на итерации 12000+ - на других машинах это может произойти сразу после итерации 7, 15 или 56 или никогда).

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

И поскольку я могу наблюдать такое поведение как в Windows XP, так и на других устройствах. я совершенно уверен, что это стандартная NTFS-система, «как задумано» Microsoft. Поэтому мне нужно решение, которое позволяет ОС завершить удаление до того, как я попытаюсь продолжить, желательно без ненужной привязки циклов процессора и без чрезмерных накладных расходов на просмотр папки, в которой находится этот файл (если возможно).

1 Да, этот цикл возвращается в случае сбоя записи или закрытия утечек, но поскольку это простое тестовое приложение консоли, само приложение завершается, и Windows гарантирует, что все дескрипторы закрываются ОС по завершении процесса. Так что утечек здесь нет.

bool DeleteFileNowA(const char * pszFilename)
{
    // Determine the path in which to store the temp filename
    char szPath[MAX_PATH];
    strcpy(szPath, pszFilename);
    PathRemoveFileSpecA(szPath);

    // Generate a guaranteed to be unique temporary filename to house the pending delete
    char szTempName[MAX_PATH];
    if (!GetTempFileNameA(szPath, ".xX", 0, szTempName))
        return false;

    // Move the real file to the dummy filename
    if (!MoveFileExA(pszFilename, szTempName, MOVEFILE_REPLACE_EXISTING))
        return false;

    // Queue the deletion (the OS will delete it when all handles (ours or other processes) close)
    if (!DeleteFileA(szTempName))
        return false;

    return true;
}

31
задан Peter Mortensen 23 August 2019 в 13:25
поделиться