首页 > C# > C#无法使用 Directory.Delete(path, true) 删除目录

C#无法使用 Directory.Delete(path, true) 删除目录

上一篇 下一篇

我正在使用 .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)
    ...

我对该方法有时会抛出并不感到惊讶,但是当递归为真时,我很惊讶地收到此特定消息。(我知道目录不为空。

有没有理由我会看到这个而不是访问违规异常?

分割线

网友回答:

编者按:尽管此答案包含一些有用的信息,但实际上对 的运作是不正确的。请阅读此答案的评论以及此问题的其他答案。Directory.Delete


我以前遇到过这个问题。

问题的根源是此函数不会删除目录结构中的文件。因此,您需要做的是创建一个函数,该函数在删除目录本身之前删除目录结构中的所有文件,然后删除所有目录。我知道这违背了第二个参数,但这是一种更安全的方法。此外,您可能希望在删除文件之前立即从文件中删除只读访问属性。否则,这将引发异常。

只需将此代码放入您的项目中即可。

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:

分割线

网友回答:

如果您尝试递归删除目录并且目录在资源管理器中打开,将被删除,但即使您去查看时它为空,您也会收到错误“目录不为空”。任何应用程序(包括资源管理器)的当前目录都保留该目录的句柄。当您调用 时,它会从下到上删除:,然后 。如果在资源管理器中打开,资源管理器将检测删除 ,向上更改目录并清理打开的句柄。由于文件系统以异步方式运行,因此操作会因与资源管理器冲突而失败。aabbaDirectory.Delete(true)babbcd ..Directory.Delete

不完整的解决方案

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

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

但这仅在打开的目录是您要删除的目录的直接子目录时才有效。如果在资源管理器中打开,并且您在 上使用它,则此技术将在删除和 .abcdadc

更好的解决方案

此方法将处理深层目录结构的删除,即使在资源管理器中打开了某个较低级别的目录也是如此。

/// <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

您可以通过在块的开头添加 a 来减少在典型条件下引发和捕获的异常数量。此外,在系统负载过重的情况下,您可能会通过两次尝试并失败。将此解决方案视为更可靠的递归删除的起点。Thread.Sleep(0)tryDirectory.Delete

一般答案

此解决方案仅解决与 Windows 资源管理器交互的特殊性。如果您想要一个坚如磐石的删除操作,要记住的一件事是,任何内容(病毒扫描程序等)都可以随时打开您尝试删除的内容的句柄。所以你必须稍后再试。稍后删除对象以及尝试多少次取决于删除对象的重要性。正如 MSDN 所指出的,

可靠的文件迭代代码必须考虑到文件系统的许多复杂性

这个无辜的声明,只提供了NTFS参考文档的链接,应该让你的头发竖起来。

(编辑:很多。这个答案最初只有第一个不完整的解决方案。

分割线

网友回答:

在继续之前,请检查您控制的以下原因:

  • 该文件夹是否设置为进程的当前目录?如果是,请先将其更改为其他内容。
  • 您是否从该文件夹中打开了文件(或加载了 DLL)?(并且忘记关闭/卸载它)

否则,请检查您无法控制的以下合法原因:

  • 该文件夹中有标记为只读的文件。
  • 您没有其中一些文件的删除权限。
  • 文件或子文件夹在资源管理器或其他应用中打开。

如果以上任何一种是问题所在,您应该在尝试改进删除代码之前了解为什么会发生这种情况。你的应用应该删除只读或无法访问的文件吗?谁这样标记它们,为什么?

一旦排除了上述原因,仍然存在虚假故障的可能性。如果有人持有要删除的任何文件或文件夹的句柄,则删除将失败,并且有人可能枚举文件夹或读取其文件的原因有很多:

  • 搜索索引器
  • 防病毒
  • 备份软件

处理虚假故障的一般方法是多次尝试,在尝试之间暂停。你显然不想永远尝试下去,所以你应该在一定次数的尝试后放弃,要么抛出异常,要么忽略错误。喜欢这个:

private static void DeleteRecursivelyWithMagicDust(string destinationDir) {
    const int magicDust = 10;
    for (var gnomes = 1; gnomes <= magicDust; gnomes++) {
        try {
            Directory.Delete(destinationDir, true);
        } catch (DirectoryNotFoundException) {
            return;  // good!
        } catch (IOException) { // System.IO.IOException: The directory is not empty
            System.Diagnostics.Debug.WriteLine("Gnomes prevent deletion of {0}! Applying magic dust, attempt #{1}.", destinationDir, gnomes);

            // see http://stackoverflow.com/questions/329355/cannot-delete-directory-with-directory-deletepath-true for more magic
            Thread.Sleep(50);
            continue;
        }
        return;
    }
    // depending on your use case, consider throwing an exception here
}

在我看来,这样的帮助程序应该用于所有删除,因为总是可能的虚假故障。但是,您应该根据用例调整此代码,而不仅仅是盲目复制它。

我的应用程序生成的内部数据文件夹(位于 %LocalAppData% 下)出现了虚假故障,因此我的分析如下所示:

  1. 该文件夹仅由我的应用程序控制,用户没有正当理由在该文件夹中将内容标记为只读或无法访问,因此我不尝试处理这种情况。
  2. 那里没有有价值的用户创建的东西,因此没有错误地强行删除某些内容的风险。
  3. 作为一个内部数据文件夹,我不希望它在资源管理器中打开,至少我觉得不需要专门处理这种情况(即我可以通过支持处理这种情况)。
  4. 如果所有尝试都失败,我选择忽略该错误。最坏的情况是,该应用程序无法解压缩一些较新的资源,崩溃并提示用户联系支持人员,只要不经常发生,这对我来说是可以接受的。或者,如果应用程序没有崩溃,它会留下一些旧数据,这对我来说也是可以接受的。
  5. 我选择将重试限制为 500 毫秒 (50 * 10)。这是一个在实践中行之有效的任意阈值;我希望阈值足够短,以便用户不会终止应用程序,认为它已停止响应。另一方面,半秒钟的时间足以让罪犯完成处理我的文件夹。从有时甚至可以接受的其他SO答案来看,很少有用户会遇到多次重试。Sleep(0)
  6. 我每 50 毫秒重试一次,这是另一个任意数字。我觉得如果我尝试删除文件时正在处理(索引、检查)文件,那么在我的情况下,50ms 大约是期望处理完成的正确时间。此外,50 毫秒足够小,不会导致明显的减速;同样,在许多情况下似乎已经足够了,所以我们不想拖延太多。Sleep(0)
  7. 代码会在出现任何 IO 异常时重试。我通常不希望访问 %LocalAppData 时出现任何异常,所以我选择了简单性,并接受了在发生合法异常的情况下延迟 500 毫秒的风险。我也不想找到一种方法来检测我要重试的确切异常。

模板简介:该模板名称为【C#无法使用 Directory.Delete(path, true) 删除目录】,大小是暂无信息,文档格式为.编程语言,推荐使用Sublime/Dreamweaver/HBuilder打开,作品中的图片,文字等数据均可修改,图片请在作品中选中图片替换即可,文字修改直接点击文字修改即可,您也可以新增或修改作品中的内容,该模板来自用户分享,如有侵权行为请联系网站客服处理。欢迎来懒人模板【C#】栏目查找您需要的精美模板。

相关搜索
  • 下载密码 lanrenmb
  • 下载次数 471次
  • 使用软件 Sublime/Dreamweaver/HBuilder
  • 文件格式 编程语言
  • 文件大小 暂无信息
  • 上传时间 04-20
  • 作者 网友投稿
  • 肖像权 人物画像及字体仅供参考
栏目分类 更多 >
热门推荐 更多 >
微信图片 微信公众平台 响应式 微信模板 微信素材 单页式简历模板 企业网站 html5 微信文章 自适应
您可能会喜欢的其他模板