我使用。net 3.5,试图递归删除目录使用:

Directory.Delete(myPath, true);

我的理解是,如果文件正在使用或存在权限问题,这应该抛出,但否则它应该删除目录及其所有内容。

然而,我偶尔会遇到这样的情况:

System.IO.IOException: The directory is not empty.
    at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
    at System.IO.Directory.DeleteHelper(String fullPath, String userPath, Boolean recursive)
    at System.IO.Directory.Delete(String fullPath, String userPath, Boolean recursive)
    ...

我并不惊讶于这个方法有时会抛出错误,但我惊讶于当递归为真时得到这个特定的消息。(我知道目录不是空的。)

是否有一个原因,我将看到这个而不是AccessViolationException?


当前回答

If you are trying to recursively delete directory a and directory a\b is open in Explorer, b will be deleted but you will get the error 'directory is not empty' for a even though it is empty when you go and look. The current directory of any application (including Explorer) retains a handle to the directory. When you call Directory.Delete(true), it deletes from bottom up: b, then a. If b is open in Explorer, Explorer will detect the deletion of b, change directory upwards cd .. and clean up open handles. Since the file system operates asynchronously, the Directory.Delete operation fails due to conflicts with Explorer.

不完整的解决方案

我最初发布了以下解决方案,想法是中断当前线程,让资源管理器有时间释放目录句柄。

// incomplete!
try
{
    Directory.Delete(path, true);
}
catch (IOException)
{
    Thread.Sleep(0);
    Directory.Delete(path, true);
}

但是,只有当打开的目录是您正在删除的目录的直接子目录时,这才有效。如果a\b\c\d在资源管理器中是打开的,并且你在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。尚不清楚第一次删除尝试是否为第二次成功的删除尝试铺平了道路,或者仅仅是抛出/捕获异常导致的时间延迟,允许文件系统赶上。

通过在try块的开头添加Thread.Sleep(0),可以减少在典型条件下抛出和捕获的异常数量。此外,在系统负载较重的情况下,您可能会同时浏览这两个目录。删除尝试并失败。可以将此解决方案作为更健壮的递归删除的起点。

一般的答案

此解决方案仅处理与Windows资源管理器交互的特性。如果你想要一个坚如磐石的删除操作,有一件事要记住,任何东西(病毒扫描程序,无论什么)都可能在任何时候对你试图删除的东西有一个开放的句柄。所以你得稍后再试。多久之后,以及尝试了多少次,取决于删除对象的重要性。正如MSDN所示,

健壮的文件迭代代码必须考虑到许多复杂性 文件系统的。

这个无辜的声明,只提供了一个到NTFS参考文档的链接,应该会让你汗毛直竖。

(编辑:很多。这个答案原来只有第一个不完全解。)

其他回答

在Windows上,当目录(或任何子目录)中的文件路径长度大于260个符号时,就会出现此问题。

在这种情况下,您需要删除\\\\?\C:\mydir而不是C:\mydir。 大约260个符号的限制,你可以在这里阅读。

当方法是异步的并像这样编码时,我解决了上述问题的一个可能实例:

// delete any existing update content folder for this update
if (await fileHelper.DirectoryExistsAsync(currentUpdateFolderPath))
       await fileHelper.DeleteDirectoryAsync(currentUpdateFolderPath);

用这个:

bool exists = false;                
if (await fileHelper.DirectoryExistsAsync(currentUpdateFolderPath))
    exists = true;

// delete any existing update content folder for this update
if (exists)
    await fileHelper.DeleteDirectoryAsync(currentUpdateFolderPath);

结论?去除用于检查存在的句柄有一些异步的方面,微软无法与之对话。这就好像if语句中的异步方法让if语句充当using语句一样。

我在特尔斐也遇到过同样的问题。最终的结果是我自己的应用程序锁定了我想要删除的目录。不知何故,当我写入目录(一些临时文件)时,目录被锁定了。

问题在于,在删除它之前,我对它的父目录做了一个简单的更改。

不删除文件的递归目录删除当然是意外的。我的解决办法是:

public class IOUtils
{
    public static void DeleteDirectory(string directory)
    {
        Directory.GetFiles(directory, "*", SearchOption.AllDirectories).ForEach(File.Delete);
        Directory.Delete(directory, true);
    }
}

我经历过这样做有帮助的案例,但一般来说,Directory。Delete在递归删除时删除目录中的文件,如msdn中所述。

作为Windows资源管理器的用户,我偶尔也会遇到这种不正常的行为:有时我无法删除文件夹(它认为荒谬的消息是“拒绝访问”),但当我向下钻取并删除较低的项目时,我也可以删除上面的项目。所以我猜上面的代码处理的是操作系统异常——而不是基类库问题。

在Windows资源管理器中选择路径或子文件夹似乎足以阻止目录的一次执行。Delete(path, true),抛出如上所述的IOException和死亡,而不是引导Windows资源管理器到父文件夹并按预期进行。