C ++ / Win32: как ждать завершения отложенного удаления? - PullRequest
31 голосов
/ 22 сентября 2010

Решено:
* Работоспособное решение: @ sbi
* Объяснение того, что действительно происходит: @ Hans
* Объяснение того, почему OpenFile не проходит через "УДАЛИТЬ ОЖИДАНИЕ":@Benjamin

Проблема:
Наше программное обеспечение в значительной степени является механизмом интерпретации для проприетарного языка сценариев.Этот язык сценариев имеет возможность создавать файл, обрабатывать его, а затем удалять файл.Это все отдельные операции, и между этими операциями не остается открытых файловых дескрипторов.(т.е. во время создания файла создается дескриптор, используется для записи, затем закрывается. Во время обработки файла отдельный дескриптор файла открывает файл, читает из него и закрывается в 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 и чертовски просто.Я создаю файл, пишу в него, закрываю дескриптор, удаляю его, ополаскиваю, повторяю ...

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

Вопросы:
*Есть ли способ дождаться завершения удаления?
* Есть ли способ определить, что файл ожидает удаления?

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

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

Мне не удалось различить аргументы CreateFile (), которые явно разрешают этот эфир.Независимо от того, какие аргументы есть у CreateFile, на самом деле не совсем нормально открывать файл для любого доступа, когда файл ожидает удаления.И так как я могу видеть это поведение как на компьютере с XP, так и на компьютере с 64-разрядной ОС Win7, я совершенно уверен, что это основное поведение 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;
}

Ответы [ 12 ]

1 голос
/ 22 сентября 2010

Если CreateFile возвращает INVALID_HANDLE_VALUE, вы должны определить, что GetLastError возвращает в вашей конкретной ситуации (в ожидании удаления), и вернуться к CreateFile на основе только этого кода ошибки.

Редактировать

Купит ли вам флаг FILE_FLAG_DELETE_ON_CLOSE что-нибудь?

0 голосов
/ 22 сентября 2010

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

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

Я бы предпочел перекрывающийся ввод-вывод, но там CloseHandle() и DeleteFile() не обрабатывают перекрывающиеся операции :(

...