我使用。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?


当前回答

您可以通过运行以下命令来重现错误:

Directory.CreateDirectory(@"C:\Temp\a\b\c\");
Process.Start(@"C:\Temp\a\b\c\");
Thread.Sleep(1000);
Directory.Delete(@"C:\Temp\a\b\c");
Directory.Delete(@"C:\Temp\a\b");
Directory.Delete(@"C:\Temp\a");

当试图删除目录'b'时,它抛出IOException“目录不是空的”。这很愚蠢,因为我们刚刚删除了目录'c'。

在我的理解中,解释是目录'c'被标记为已删除。但是系统中还没有提交删除操作。系统已经回复任务已经完成,而实际上它还在处理中。系统可能会等待文件资源管理器已经关注到父目录才提交删除。

如果你查看删除函数(http://referencesource.microsoft.com/#mscorlib/system/io/directory.cs)的源代码,你会发现它使用本机Win32Native。RemoveDirectory函数。这种不等待行为在这里被注意到:

RemoveDirectory函数在关闭时标记要删除的目录。因此,直到关闭该目录的最后一个句柄,才会删除该目录。

(http://msdn.microsoft.com/en-us/library/windows/desktop/aa365488 (v = vs.85) . aspx)

休眠和重试是解决方案。参考ryascl的溶液。

其他回答

编者注:尽管这个答案包含了一些有用的信息,但关于Directory.Delete的工作原理实际上是不正确的。请阅读这个答案的评论,以及这个问题的其他答案。


我以前遇到过这个问题。

The root of the problem is that this function does not delete files that are within the directory structure. So what you'll need to do is create a function that deletes all the files within the directory structure then all the directories before removing the directory itself. I know this goes against the second parameter but it's a much safer approach. In addition, you will probably want to remove READ-ONLY access attributes from the files right before you delete them. Otherwise that will raise an exception.

只需将这些代码放入您的项目中。

public static void DeleteDirectory(string target_dir)
{
    string[] files = Directory.GetFiles(target_dir);
    string[] dirs = Directory.GetDirectories(target_dir);

    foreach (string file in files)
    {
        File.SetAttributes(file, FileAttributes.Normal);
        File.Delete(file);
    }

    foreach (string dir in dirs)
    {
        DeleteDirectory(dir);
    }

    Directory.Delete(target_dir, false);
}

另外,对我个人来说,我对允许删除的机器区域添加了一个限制,因为你希望有人在C:\ windows (%WinDir%)或C:\。

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

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

你是否可能有一个竞争条件,另一个线程或进程正在向目录中添加文件:

顺序是:

删除进程A:

清空目录 删除(现在是空的)目录。

如果其他人在1和2之间添加了一个文件,那么2可能会抛出列出的异常?

您可以通过运行以下命令来重现错误:

Directory.CreateDirectory(@"C:\Temp\a\b\c\");
Process.Start(@"C:\Temp\a\b\c\");
Thread.Sleep(1000);
Directory.Delete(@"C:\Temp\a\b\c");
Directory.Delete(@"C:\Temp\a\b");
Directory.Delete(@"C:\Temp\a");

当试图删除目录'b'时,它抛出IOException“目录不是空的”。这很愚蠢,因为我们刚刚删除了目录'c'。

在我的理解中,解释是目录'c'被标记为已删除。但是系统中还没有提交删除操作。系统已经回复任务已经完成,而实际上它还在处理中。系统可能会等待文件资源管理器已经关注到父目录才提交删除。

如果你查看删除函数(http://referencesource.microsoft.com/#mscorlib/system/io/directory.cs)的源代码,你会发现它使用本机Win32Native。RemoveDirectory函数。这种不等待行为在这里被注意到:

RemoveDirectory函数在关闭时标记要删除的目录。因此,直到关闭该目录的最后一个句柄,才会删除该目录。

(http://msdn.microsoft.com/en-us/library/windows/desktop/aa365488 (v = vs.85) . aspx)

休眠和重试是解决方案。参考ryascl的溶液。

有一件重要的事情需要提一下(我把它作为注释添加了进去,但是不允许我这样做),那就是重载的行为从。net 3.5改变到了。net 4.0。

Directory.Delete(myPath, true);

从。net 4.0开始,它会删除文件夹中的文件,但在3.5中不会。在MSDN文档中也可以看到这一点。

net 4.0

删除指定的目录,如果指定,删除该目录中的任何子目录和文件。

net 3.5

删除一个空目录,如果指定,删除目录中的任何子目录和文件。