【问题标题】:DeleteFile fails on recently closed fileDeleteFile 在最近关闭的文件上失败
【发布时间】:2010-12-17 17:09:34
【问题描述】:

我有一个单线程程序(C++、Win32、NTFS),它首先创建一个相当长的临时文件,关闭它,打开读取,读取,再次关闭并尝试使用DeleteFile() 删除。

通常会顺利进行,但有时DeleteFile() 会失败,GetLastError() 返回 ERROR_ACCESS_DENIED。文件肯定不是只读的。它发生在任何大小的文件上,但概率随着文件大小而增加。

任何想法可能会锁定文件?我尝试使用 WinInternals 工具进行检查,没有发现任何可疑之处。

【问题讨论】:

  • 您确定在尝试删除之前正确关闭了文件吗?你错过了任何把手吗?
  • 正如我所说,我什至使用 WinInternals 工具进行了检查。所有打开都与关闭配对,但删除失败。并且添加 sleep for 1 sec 可以解决问题。
  • 可能是 windows 有问题,但我对此表示怀疑。如果添加sleep 让它工作应该没问题^^
  • 通常,如果在某处添加 sleep() 调用会使问题消失,则需要摆脱 sleep() 调用并正确解决问题。它会回来的。我从未见过这条规则的例外情况。
  • 这并没有提供问题的答案。要批评或要求作者澄清,请在其帖子下方发表评论。

标签: c++ file winapi delete-file


【解决方案1】:

Windows 因这个问题而臭名昭著。 SQLite 通过每 100 毫秒重试一次删除操作来处理该问题,直到最大次数。

我相信,如果您确定没有打开的句柄,那么在您的实现中执行此操作会在防病毒软件等打开文件时为您省去一些麻烦。

供参考,来自SQLite来源的评论:

/*                                                                     
** Delete the named file.                                              
**                                                                     
** Note that windows does not allow a file to be deleted if some other
** process has it open.  Sometimes a virus scanner or indexing program
** will open a journal file shortly after it is created in order to do
** whatever it does.  While this other process is holding the          
** file open, we will be unable to delete it.  To work around this     
** problem, we delay 100 milliseconds and try to delete again.  Up     
** to MX_DELETION_ATTEMPTs deletion attempts are run before giving     
** up and returning an error.                                          
*/

【讨论】:

    【解决方案2】:

    您可能有竞争条件。 1. 请求操作系统写入数据。 2. 要求操作系统关闭文件。这会提示最终缓冲区刷新。在缓冲区刷新完成之前,不会关闭文件。同时,操作系统将在缓冲区刷新时将控制权返回给程序。 3. 请求操作系统删除该文件。如果刷新还没有完成,那么文件仍然是打开的并且请求被拒绝。

    【讨论】:

      【解决方案3】:
      #include <iostream>
      #include <windows.h>
      
      int main(int argc, const char * argv[])
      {
          // Get a pointer to the file name/path
          const char * pFileToDelete = "h:\\myfile.txt";
          bool RemoveDirectory("h:\\myfile.txt");
      
          // try deleting it using DeleteFile
          if(DeleteFile(pFileToDelete ))
          {
              // succeeded
              std::cout << "Deleted file"  << std::endl;
          }
          else
          {
              // failed
              std::cout << "Failed to delete the file" << std::endl;
          }
          std::cin.get();
          return 0;
      }  
      

      【讨论】:

        【解决方案4】:

        我相信这在Windows Internals 中有介绍。简而言之,即使您在文件句柄上调用了 CloseHandle,内核可能仍然有未完成的引用,需要几毫秒才能关闭。

        完成后删除文件的更可靠方法是在打开最后一个句柄时使用 FILE_FLAG_DELETE_ON_CLOSE 标志。如果您可以避免在读取/写入之间关闭文件,这会更好。

        #include <windows.h>
        #include <stdio.h>
        
        int wmain(int argc, wchar_t** argv)
        {
            LPCWSTR fileName = L"c:\\temp\\test1234.bin";
        
            HANDLE h1 = CreateFileW(
                fileName,
                GENERIC_WRITE,
                // make sure the next call to CreateFile can succeed if this handle hasn't been closed yet
                FILE_SHARE_READ | FILE_SHARE_DELETE,
                NULL,
                CREATE_ALWAYS,
                FILE_FLAG_SEQUENTIAL_SCAN | FILE_ATTRIBUTE_TEMPORARY,
                NULL);
            if (h1 == INVALID_HANDLE_VALUE)
            {
                fprintf(stderr, "h1 failed: 0x%x\n", GetLastError());
                return GetLastError();
            }
        
            HANDLE h2 = CreateFileW(
                fileName,
                GENERIC_READ,
                // FILE_SHARE_WRITE is required in case h1 with GENERIC_WRITE access is still open
                FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
                NULL,
                OPEN_EXISTING,
                // tell the OS to delete the file as soon as it is closed, no DeleteFile call needed
                FILE_FLAG_DELETE_ON_CLOSE | FILE_FLAG_SEQUENTIAL_SCAN | FILE_ATTRIBUTE_TEMPORARY,
                NULL);
            if (h2 == INVALID_HANDLE_VALUE)
            {
                fprintf(stderr, "h2 failed: 0x%x\n", GetLastError());
                return GetLastError();
            }
        
            return 0;
        }
        

        【讨论】:

        • 如果另一个进程打开了同一个文件,这不会失败吗,鉴于文档中的以下描述? "如果文件存在现有的打开句柄,则调用将失败,除非它们都以 FILE_SHARE_DELETE 共享模式打开。"
        • 是的,这就是为什么我建议不要在写入和读取之间关闭文件句柄。使用 FILE_FLAG_DELETE_ON_CLOSE 创建第一个句柄,然后如果您确实需要没有写入权限的文件句柄,请使用 ReOpenFile 或 DuplicateHandle。
        • 也许我今天很慢,但是如果有人在最​​后一次调用 CreateFile 之前偷偷打开文件,这不会仍然是个问题吗?最后一次调用仍然会失败。
        • 您想在 single 调用 CreateFile 时使用 FILE_FLAG_DELETE_ON_CLOSE,那么就没有文件泄漏的机会。否则,所有赌注都被取消。如果您需要具有不同标志或权限的文件句柄,那么您应该使用 ReOpenFile。
        • 明白了。这不是问题所在,但似乎有一个很好的论据可以以这种方式构建代码。
        【解决方案5】:

        只是一个疯狂的猜测 - 您是否安装了任何防病毒软件?您是否尝试过禁用其中的任何实时保护功能?

        【讨论】:

          【解决方案6】:

          也许更改仍在缓存中,尚未保存?

          您可以通过在文件句柄上添加 WaitForSingleObject 来确定这一点。

          【讨论】:

          • 写入缓存对应用程序是透明的。它不应导致行为改变。
          【解决方案7】:

          在调用 DeleteFile() 之前添加 MessageBox() 调用,当它出现时,运行 sysinternals 工具 Process Explorer。 搜索文件的打开句柄。很可能您还没有关闭文件的所有句柄...

          【讨论】:

          • 这就是我的起点。没有把手。所以我记录了对文件的所有访问,没有什么特别的。
          • 这听起来像是一个竞争条件(可能在毫秒的数量级),所以除非你冻结一切,否则你可能无法以这种方式重现错误。 (但尝试肯定有助于缩小可能性。)
          猜你喜欢
          • 1970-01-01
          • 2022-08-18
          • 2013-07-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-02
          • 1970-01-01
          • 2021-07-21
          相关资源
          最近更新 更多