已解决:
- 可行的解决方案:sbi 的答案
- 真实情况的解释:Hans 的答案
- OpenFile 为什么没有通过“DELETE PENDING”状态的解释:Benjamin 的答案
问题描述:
我们的软件主要是用来解释一种专有脚本语言的引擎。该脚本语言具有创建文件、处理文件和删除文件的功能,这些都是单独的操作,并且在这些操作之间没有保持文件句柄处于打开状态。
(即在文件创建期间,会创建一个句柄,用于写入,然后关闭。在文件处理部分,另一个文件句柄打开文件,从中读取内容,当达到文件末尾时关闭。最后,删除使用的是 ::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()。 但是在WaitForSingleObject执行之前,HFILE总是被关闭,因此WaitForSingleObject始终返回WAIT_FAILED。 显然,等待已关闭的句柄是无效的。
我可以等待文件所在文件夹的更改通知。但是,这看起来像是一种极其消耗资源的解决方法,只会在偶尔出现的问题上使用(例如:在我的Windows 7 x64 E6600 PC上测试时,通常在第12000+次迭代中失败--在其他机器上,可能发生在第7或15或56次迭代中,也可能永远不会发生)。
我无法确定有任何CreateFile()参数可以显式地允许这种情况。 无论CreateFile具有什么参数,当文件处于挂起删除状态时,它都不能打开文件以进行任何访问操作。
由于我在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;
}
MsMpEng
阻止了这个文件。我已经发现了微软的确认,索引/ 杀毒软件可以阻止该文件。因此,我现在习惯于在重试循环中将其删除,或者最好创建新的临时文件而不是重用单个名称,希望单个删除最终会生效。现在,我遇到了一个类似的问题。在gcc p.c > p.exe && p && gcc p2.c > p.exe && p ...
中,p.exe
刚刚终止,被阻止了,但是 filemon 中没有任何东西,只有p
进程访问该文件。 - Val