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

  • 使用 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)
    {
    }
}
有帮助吗?

解决方案

这是一个扩展方法,它设置 AttributesNormal 递归地,然后删除项目:

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

其他提示

避免递归调用的最简单方法是利用 AllDirectories 获取时的选项 FileSystemInfos,像这样:

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

尝试这个,

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

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

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

请注意,~ 是一个按位逻辑运算符,它返回给定二进制值的补码。我还没有测试过这个,但它应该有效。

谢谢!

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

我不喜欢第一种方法(directory.delete)的情况是,有些子目录也包含只读文件,并且它们的子目录也包含只读文件,依此类推。似乎您必须递归地为目录和所有子目录中的每个文件关闭该标志。

使用第二种方法,您可以只删除第一个目录,并且不会检查文件是否是只读的。然而,这是我第一次在 C# 中使用 WMI,所以我不太习惯它。因此,我不确定何时对其他应用程序使用 WMI 方法,而不是仅使用 System.IO 方法。

从表面上看,使用 WMI 方法似乎比迭代整个文件系统更有效(例如假设目录有数十个或数千个文件)。但我不知道WMI也不做迭代。如果确实如此,那么更接近金属(再次假设)它应该会更有效。

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

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

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

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# 删除具有长路径的文件夹

为了跟进 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();
  }
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top