【发布时间】:2011-04-15 09:52:55
【问题描述】:
已解决:
- 可行的解决方案:sbi's answer
- 真实情况的解释:Hans's answer
- 解释为什么 OpenFile 不通过“DELETE PENDING”:Benjamin's answer
问题:
我们的软件在很大程度上是专有脚本语言的解释器引擎。该脚本语言能够创建文件、处理文件,然后删除文件。这些都是独立的操作,在这些操作之间没有文件句柄保持打开状态。
(即在文件创建期间,创建一个句柄,用于写入,然后关闭。在文件处理部分,一个单独的文件句柄打开文件,从中读取,并在 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,而且非常简单。我创建一个文件,写入它,关闭句柄,删除它,冲洗,重复......
但是在某个地方,我会在 CreateFile() 调用期间收到拒绝访问 (5) 错误。查看 sysinternal 的 ProcessMonitor,我可以看到根本问题是在我尝试再次创建文件时,文件上有一个挂起的删除。
问题:
- 有没有办法等待删除完成?
- 有没有办法检测文件正在等待删除?
我们已经尝试了第一个选项,只需在 HFILE 上使用 WaitForSingleObject()。但是 HFILE 总是在 WaitForSingleObject 执行之前关闭,所以 WaitForSingleObject 总是返回 WAIT_FAILED。显然,尝试等待关闭的句柄是行不通的。
我可以等待文件所在文件夹的更改通知。但是,对于仅偶尔出现的问题来说,这似乎是一个极其开销密集的杂项(即:在我的 Windows 7 x64 E6600 上的测试中PC 它通常在迭代 12000+ 时失败——在其他机器上,它可能在迭代 7 或 15 或 56 时立即发生,或者永远不会发生)。
我无法辨别任何明确允许该以太的 CreateFile() 参数。无论 CreateFile 有什么参数,当文件等待删除时,打开文件以供 any 访问确实是不行的。
由于我在 Windows XP 机器和 x64 Windows 7 机器上都可以看到这种行为,因此我很确定这是 Microsoft“按预期”的核心 NTFS 行为。因此,我需要一个解决方案,允许操作系统在我尝试继续之前完成删除,最好不要不必要地占用 CPU 周期,并且不会因查看该文件所在的文件夹而产生极大的开销(如果可能的话)。
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;
}
【问题讨论】:
-
您确定所有句柄都已关闭?因为您在 MSDN 上准确描述了您所写的内容:“如果您对因先前调用 DeleteFile 而等待删除的文件调用 CreateFile,则该函数将失败。操作系统会延迟文件删除,直到该文件的所有句柄都关闭.GetLastError 返回 ERROR_ACCESS_DENIED。"
-
我创建了一个控制台应用程序,它在循环中执行上述代码。它最终失败了……通常在迭代 12000-15000 左右。所以除非你看到上面 8 行代码泄漏了句柄,否则我认为这是不可能的(至少对于这个测试小程序来说)。
-
@Mordachai:我已经看到这个并删除了我的评论。
-
我遇到了这个由Windows Search服务引起的问题,它会注意到我创建了一个目录并锁定它以对其进行索引,同时我试图删除它并得到错误5。很容易通过在启用索引器的循环中创建和删除目录/文件来重现此问题。
-
是
MsMpEngin my case 阻止了该文件。我发现 MS 确认 index/antivirus SW can block the file。现在,我因此习惯于在重试循环中删除或更好地创建新的临时文件而不是重复使用单个名称,希望单个删除最终会生效。现在,我遇到了类似的问题。Gcc p2在gcc p.c > p.exe && p && gcc p2.c > p.exe && p ...中失败,因为刚刚终止的 p.exe 被阻止,filemon 中没有任何内容,但 p 进程访问该文件。