如何删除C#中包含只读文件的目录?

如何删除C#中包含只读文件的目录?,c#,directory,readonly,delete-directory,C#,Directory,Readonly,Delete Directory,我需要删除一个包含只读文件的目录。哪种方法更好: 使用DirectoryInfo.Delete(),或 ManagementObject.InvokeMethod(“删除”) 使用DirectoryInfo.Delete(),我必须手动关闭每个文件的只读属性,但是ManagementObject.InvokeMethod(“删除”)似乎不需要。是否存在一种情况比另一种更可取 示例代码(test.txt为只读) 第一种方式: 第二种方式: 我想说的是,您的第一种方法看起来更加明确和可读。第二种

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

  • 使用
    DirectoryInfo.Delete()
    ,或

  • ManagementObject.InvokeMethod(“删除”)

使用
DirectoryInfo.Delete()
,我必须手动关闭每个文件的只读属性,但是
ManagementObject.InvokeMethod(“删除”)
似乎不需要。是否存在一种情况比另一种更可取

示例代码(test.txt为只读)

第一种方式: 第二种方式:
我想说的是,您的第一种方法看起来更加明确和可读。第二种方法闻起来像反射,不是类型安全的,看起来很奇怪。
ManagementObject
可以表示多个内容,因此
.InvokeMethod(“Delete”)
实际上删除一个目录并不明显。

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

// 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));
请注意,~是一个按位逻辑运算符,返回给定二进制值的补码。我还没有测试过这个,但它应该可以工作


谢谢

这里有一个扩展方法,它递归地将
属性设置为
正常
,然后删除项目:

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();
}

从表面上看,使用WMI方法似乎比在整个文件系统上迭代更有效(例如,假设目录中有10个成千上万的文件)。但我不知道WMI也不做迭代。如果是这样的话,离金属越近(同样是假设),应该效率越高

为了美观起见,我承认递归方法很酷


性能测试应该回答效率问题。如果封装在DirectoryInfo的扩展方法中,这两种方法都可能非常优雅。

另一种方法不需要递归

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);
}
公共静态目录(字符串路径)
{
目录信息根;
堆叠文件夹;
目录信息fol;
fols=新堆栈();
root=新目录信息(路径);
fols.推(根);
而(fols.Count>0)
{
fol=fols.Pop();
fol.Attributes=fol.Attributes&~(FileAttributes.Archive | FileAttributes.ReadOnly | FileAttributes.Hidden);
foreach(fol.GetDirectories()中的DirectoryInfo d)
{
按下按钮(d);
}
foreach(fol.GetFiles()中的FileInfo f)
{
f、 Attributes=f.Attributes&~(FileAttributes.Archive | FileAttributes.ReadOnly | FileAttributes.Hidden);
f、 删除();
}
}
root.Delete(true);
}
试试这个

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);
}

这里是另一个避免自身递归的解决方案

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”方法的最后一行,并单独使用delete


另一方面,虽然这样做有效,但我在删除“太长”的路径时遇到了问题,最终使用了此处发布的robocopy解决方案:

避免递归调用的最简单方法是在获取
文件系统信息时使用
所有目录
选项,如下所示:

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);
}

为了跟进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();
  }

我针对NET framework 3.5和NET framework 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();

很好的解决方案,我从你那里偷了它,只是在dirInfo.Delete()之前添加了一个检查;要查看目录本身是否为只读,将在本例中引发异常。在这种情况下,使用此代码会删除所有文件和目录。然后,当进程退出时,两个空文件夹重新出现。我删除了它们,然后它们就消失了。我在一些其他类似问题的评论中添加了这个答案,这些问题使用的是删除RO而不是设置为正常。您的方法更好。我必须在
Delete()
操作之前添加这行代码:
fileSystemInfo.Refresh()
。否则我会得到一个异常,说目录不是空的。请注意,当文件系统信息是符号链接或连接时,我们需要跳过目录逻辑。因此,我建议在调用
GetFileSystemInfo()
之前检查
fileSystemInfo.Attributes.HasFlag(FileAttributes.ReparsePoint)
。SearchOption参数是用.NET 4.0添加到GetFileSystemInfo中的。FileInfo-fi是无用的
  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();
  }
#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();