如果您尝试递归删除目录,a
并且目录a\b
已在资源管理器中打开,b
将被删除,但a
即使您在查找时它为空,也会收到“目录不为空”错误。任何应用程序(包括Explorer)的当前目录都保留该目录的句柄。呼叫时Directory.Delete(true)
,它会从下往上删除:b
,然后a
。如果b
在Explorer中打开,则Explorer将检测到的删除b
,向上更改目录cd ..
并清理打开的句柄。由于文件系统异步运行,因此Directory.Delete
由于与资源管理器冲突而导致操作失败。
解决方案不完整
我最初发布了以下解决方案,其想法是中断当前线程以使Explorer可以释放目录句柄。
// incomplete!
try
{
Directory.Delete(path, true);
}
catch (IOException)
{
Thread.Sleep(0);
Directory.Delete(path, true);
}
但这仅在打开目录是要删除目录的直接子目录时才有效。如果a\b\c\d
在Explorer中打开并使用了此功能a
,则删除d
和后该技术将失败c
。
更好的解决方案
即使在资源管理器中打开了一个较低级别的目录,此方法也将处理深度目录结构的删除。
/// <summary>
/// Depth-first recursive delete, with handling for descendant
/// directories open in Windows Explorer.
/// </summary>
public static void DeleteDirectory(string path)
{
foreach (string directory in Directory.GetDirectories(path))
{
DeleteDirectory(directory);
}
try
{
Directory.Delete(path, true);
}
catch (IOException)
{
Directory.Delete(path, true);
}
catch (UnauthorizedAccessException)
{
Directory.Delete(path, true);
}
}
尽管需要自己进行额外的递归工作,但我们仍然必须处理在执行过程UnauthorizedAccessException
中可能发生的事情。尚不清楚第一次删除尝试是否为第二次成功尝试铺平了道路,还是仅仅是由于引发/捕获允许文件系统赶超的异常而引入的时间延迟。
通过Thread.Sleep(0)
在try
块的开头添加a ,可以减少典型情况下引发和捕获的异常的数量。此外,在系统负载较重的情况下,您可能会经历两次Directory.Delete
尝试而失败。将此解决方案视为更健壮的递归删除的起点。
一般答案
该解决方案仅解决了与Windows资源管理器进行交互的特殊性。如果要进行坚如磐石的删除操作,请牢记一件事:任何东西(病毒扫描程序等)都可以随时随地打开要删除的内容。因此,您必须稍后再试。多少时间后以及尝试多少次,取决于删除对象的重要性。如MSDN所示,
健壮的文件迭代代码必须考虑文件系统的许多复杂性。
仅提供指向NTFS参考文档的链接的这份无辜的声明应该使您站起来。
(编辑:很多。这个答案最初只有第一个不完整的解决方案。)