【问题标题】:How do I delete a directory with read-only files in C#?如何在 C# 中删除包含只读文件的目录?
【发布时间】:2010-10-11 08:20:05
【问题描述】:

我需要删除一个包含只读文件的目录。哪种方法更好:

  • 使用DirectoryInfo.Delete(),或者,

  • ManagementObject.InvokeMethod("Delete")?

使用DirectoryInfo.Delete(),我必须手动关闭每个文件的只读属性,但ManagementObject.InvokeMethod("Delete") 似乎不需要。有没有一种情况比另一种更可取?

示例代码(test.txt 是只读的)。

第一种方式:

DirectoryInfo dir = new DirectoryInfo(@"C:\Users\David\Desktop\");
dir.CreateSubdirectory("Test");

DirectoryInfo test = new DirectoryInfo(@"C:\Users\David\Desktop\Test\");
File.Copy(@"C:\Users\David\Desktop\test.txt", @"C:\Users\David\Desktop\Test\test.txt");
File.SetAttributes(@"C:\Users\David\Desktop\Test\test.txt", FileAttributes.Archive);
test.Delete(true);

第二种方式:

DirectoryInfo dir = new DirectoryInfo(@"C:\Users\David\Desktop\");
dir.CreateSubdirectory("Test");

DirectoryInfo test = new DirectoryInfo(@"C:\Users\David\Desktop\Test\");
File.Copy(@"C:\Users\David\Desktop\test.txt", @"C:\Users\David\Desktop\Test\test.txt");

string folder = @"C:\Users\David\Desktop\Test";
string dirObject = "Win32_Directory.Name='" + folder + "'";
using (ManagementObject managementObject = new ManagementObject(dirObject))
{
    managementObject.Get();
    ManagementBaseObject outParams = managementObject.InvokeMethod("Delete", null,
    null);
    // ReturnValue should be 0, else failure
    if (Convert.ToInt32(outParams.Properties["ReturnValue"].Value) != 0)
    {
    }
}

【问题讨论】:

    标签: c# directory readonly delete-directory


    【解决方案1】:

    我会说你的第一种方法看起来更明确和可读。第二种方法闻起来像反射,不是类型安全的,而且看起来很奇怪。 ManagementObject 可以代表多个东西,所以.InvokeMethod("Delete") 实际删除一个目录并不明显。

    【讨论】:

      【解决方案2】:

      最好的解决办法是将所有文件标记为非只读,然后删除目录。

      // delete/clear hidden attribute
      File.SetAttributes(filePath, File.GetAttributes(filePath) & ~FileAttributes.Hidden);
      
      // delete/clear archive and read only attributes
      File.SetAttributes(filePath, File.GetAttributes(filePath) 
          & ~(FileAttributes.Archive | FileAttributes.ReadOnly));
      

      请注意,~ 是一个按位逻辑运算符,它返回给定二进制值的补码。我没有对此进行测试,但它应该可以工作。

      谢谢!

      【讨论】:

        【解决方案3】:

        这是一个扩展方法,它将Attributes递归设置为Normal,然后删除项目:

        public static void DeleteReadOnly(this FileSystemInfo fileSystemInfo)
        {
            var directoryInfo = fileSystemInfo as DirectoryInfo;    
            if (directoryInfo != null)
            {
                foreach (FileSystemInfo childInfo in directoryInfo.GetFileSystemInfos())
                {
                    childInfo.DeleteReadOnly();
                }
            }
        
            fileSystemInfo.Attributes = FileAttributes.Normal;
            fileSystemInfo.Delete();
        }
        

        【讨论】:

        • 我有一种情况,使用此代码会删除所有文件和目录。然后当进程退出时,两个空文件夹重新出现。我手动删除它们,它们就消失了。
        • 我在其他一些类似问题的 cmets 中添加了这个答案,这些问题使用删除 RO 而不是设置为正常。你的方法更好。
        • 我必须在Delete() 操作之前添加这行代码:fileSystemInfo.Refresh()。否则我收到Exception 说目录不为空。
        • 请注意,当fileSystemInfo是符号链接或结点时,我们需要跳过目录逻辑。所以我建议在调用GetFileSystemInfos()之前检查fileSystemInfo.Attributes.HasFlag(FileAttributes.ReparsePoint)
        【解决方案4】:

        从表面上看,使用 WMI 方法似乎比遍历整个文件系统更有效(例如,假设该目录有成千上万个文件)。但我不知道 WMI 也不进行迭代。如果是这样,那么更接近金属(同样,假设)应该更有效。

        为了优雅,我承认递归方法很酷。

        性能测试应该回答效率问题。如果包装在 DirectoryInfo 的扩展方法中,两者都可以很优雅。

        【讨论】:

          【解决方案5】:

          另一种不需要递归的方法。

          public static void ForceDeleteDirectory(string path)
          {
              DirectoryInfo root;
              Stack<DirectoryInfo> fols;
              DirectoryInfo fol;
              fols = new Stack<DirectoryInfo>();
              root = new DirectoryInfo(path);
              fols.Push(root);
              while (fols.Count > 0)
              {
                  fol = fols.Pop();
                  fol.Attributes = fol.Attributes & ~(FileAttributes.Archive | FileAttributes.ReadOnly | FileAttributes.Hidden);
                  foreach (DirectoryInfo d in fol.GetDirectories())
                  {
                      fols.Push(d);
                  }
                  foreach (FileInfo f in fol.GetFiles())
                  {
                      f.Attributes = f.Attributes & ~(FileAttributes.Archive | FileAttributes.ReadOnly | FileAttributes.Hidden);
                      f.Delete();
                  }
              }
              root.Delete(true);
          }
          

          【讨论】:

            【解决方案6】:

            试试这个,

            private void DeleteRecursiveFolder(string pFolderPath)
            {
                foreach (string Folder in Directory.GetDirectories(pFolderPath))
                {
                    DeleteRecursiveFolder(Folder);
                }
            
                foreach (string file in Directory.GetFiles(pFolderPath))
                {
                    var pPath = Path.Combine(pFolderPath, file);
                    FileInfo fi = new FileInfo(pPath);
                    File.SetAttributes(pPath, FileAttributes.Normal);
                    File.Delete(file);
                }
            
                Directory.Delete(pFolderPath);
            }
            

            【讨论】:

            • FileInfo fi没用
            【解决方案7】:
            private void DeleteRecursiveFolder(DirectoryInfo dirInfo)
            {
                foreach (var subDir in dirInfo.GetDirectories())
                {
                    DeleteRecursiveFolder(subDir);
                }
            
                foreach (var file in dirInfo.GetFiles())
                {
                    file.Attributes=FileAttributes.Normal;
                    file.Delete();
                }
            
                dirInfo.Delete();
            }
            

            【讨论】:

            • 很好的解决方案,我从你那里偷来的,只是在 dirInfo.Delete();查看目录本身是否是只读的,在这种情况下会抛出异常。谢谢
            【解决方案8】:

            这是另一种避免自身递归的解决方案。

            public static void DirectoryDeleteAll(string directoryPath)
            {
                var rootInfo = new DirectoryInfo(directoryPath) { Attributes = FileAttributes.Normal };
                foreach (var fileInfo in rootInfo.GetFileSystemInfos()) fileInfo.Attributes = FileAttributes.Normal;
                foreach (var subDirectory in Directory.GetDirectories(directoryPath, "*", SearchOption.AllDirectories))
                {
                    var subInfo = new DirectoryInfo(subDirectory) { Attributes = FileAttributes.Normal };
                    foreach (var fileInfo in subInfo.GetFileSystemInfos()) fileInfo.Attributes = FileAttributes.Normal;
                }
                Directory.Delete(directoryPath, true);
            }
            

            这通过在删除之前重置文件夹和文件的属性来工作,因此您可以删除“DirectoryResetAttributes”方法的最后一行并单独使用删除。

            在相关说明中,虽然这有效,但我在删除“太长”的路径时遇到了问题,最终使用了此处发布的 robocopy 解决方案:C# deleting a folder that has long paths

            【讨论】:

              【解决方案9】:

              避免递归调用的最简单方法是在获取FileSystemInfos 时使用AllDirectories 选项,如下所示:

              public static void ForceDeleteDirectory(string path) 
              {
                  var directory = new DirectoryInfo(path) { Attributes = FileAttributes.Normal };
              
                  foreach (var info in directory.GetFileSystemInfos("*", SearchOption.AllDirectories))
                  {
                      info.Attributes = FileAttributes.Normal;
                  }
              
                  directory.Delete(true);
              }
              

              【讨论】:

              • SearchOption 参数已通过 .NET 4.0 添加到 GetFileSystemInfos。
              【解决方案10】:

              为了跟进 Vitaliy Ulantikov 的解决方案,我用重命名/移动文件夹方法对其进行了补充:

                public static void renameFolder(String sourcePath, String targetPath) {
                   try
                   {
                      if (System.IO.Directory.Exists(targetPath))
                         DeleteFileSystemInfo(new DirectoryInfo(targetPath));
                      System.IO.Directory.Move(sourcePath, targetPath);
                   }
                   catch (Exception ex)
                   {
                      Console.WriteLine("renameFolder: " + sourcePath + " " + targetPath + " " + ex.Message);
                      throw ex;
                   }
                }
              
                private static void DeleteFileSystemInfo(FileSystemInfo fsi) {
                   fsi.Attributes = FileAttributes.Normal;
                   var di = fsi as DirectoryInfo;
              
                   if (di != null)
                   {
                      foreach (var dirInfo in di.GetFileSystemInfos())
                      {
                         DeleteFileSystemInfo(dirInfo);
                      }
                   }
              
                   fsi.Delete();
                }
              

              【讨论】:

                【解决方案11】:

                NET 框架 3.5 和 NET 框架 4 及更高版本的解决方案:

                #region DeleteWithReadOnly
                internal static void DeleteWithReadOnly(this DirectoryInfo di)
                {
                   foreach (FileSystemInfo fsi in di.EnumerateFileSystemInfos("*", SearchOption.AllDirectories))
                   {
                      fsi.Attributes = FileAttributes.Normal;
                   }
                   di.Delete(true);
                }
                #endregion
                
                #region DeleteWithReadOnlyNET3_5
                internal static void DeleteWithReadOnlyNET3_5(this DirectoryInfo di)
                {
                   foreach (FileSystemInfo fsi in di.GetFiles("*", SearchOption.AllDirectories))
                   {
                      fsi.Attributes = FileAttributes.Normal;
                   }
                   di.Delete(true);
                }
                #endregion
                

                用法:

                DirectoryInfo di = new DirectoryInfo(@"C:\TMP");
                di.DeleteWithReadOnly();
                

                【讨论】:

                  猜你喜欢
                  • 2011-03-21
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2010-12-25
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多