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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/611921/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 10:15:46  来源:igfitidea点击:

How do I delete a directory with read-only files in C#?

c#directoryreadonlydelete-directory

提问by David Hodgson

I need to delete a directory that contains read-only files. Which approach is better:

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

  • Using DirectoryInfo.Delete(), or,

  • ManagementObject.InvokeMethod("Delete")?

  • 使用DirectoryInfo.Delete(), 或,

  • ManagementObject.InvokeMethod("Delete")?

With DirectoryInfo.Delete(), I have to manually turn off the read-only attribute for each file, but ManagementObject.InvokeMethod("Delete")doesn't appear to need to. Is there any situation where one is more preferable to the other?

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

Sample code (test.txt is read only).

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

First way:

第一种方式:

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

Second way:

第二种方式:

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

采纳答案by Vitaliy Ulantikov

Here is an extension method which sets Attributesto Normalrecursively, then deletes the items:

这是一个扩展方法,它设置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();
}

回答by Thomas Eyde

I would say that your first approach looks more explicit and readable. The second method smells like reflection, is not type safe and looks weird. The ManagementObjectcan represent multiple things, so it's not obvious that .InvokeMethod("Delete")actually deletes a directory.

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

回答by Thomas Eyde

The best solution is to mark all the files as non-read only, and then delete the directory.

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

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

Notice that ~ is a Bitwise logical operator which returns the complement of the given binary value. I haven't tested this, but it should work.

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

Thanks!

谢谢!

回答by Donald Byrd

On the surface, using the WMI approach seems more efficient than iterating over the entire file system (assume for example the directory has 10's of thousands of files). But I do not know that WMI also doesn't do iterations. If it does, being closer to the metal (again, assumptions) it should be more efficient.

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

For elegance, I concede the recursive method is cool.

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

Performance testing should answer the efficiency question. And either can be elegant if wrapped in an extension method of DirectoryInfo.

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

回答by jasonk

Another method without the need for recursion.

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

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

回答by Frederic Arteau

Try this,

尝试这个,

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

回答by jdvatwork

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

回答by WhoIsRich

Here is another solution that avoids recursion on itself.

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

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

This works by resettings attributes on the folders and files before the delete, so you could just remove the last line for a 'DirectoryResetAttributes' method and use delete separately.

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

On a related note, while this worked, I then had issues with deleting paths that were 'too long' and ended up using a robocopy solution posted here: C# deleting a folder that has long paths

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

回答by adrian

Simplest way of avoiding recursive calls is by utilising the AllDirectoriesoption when getting FileSystemInfos, like so:

避免递归调用的最简单方法是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);
}

回答by cdavidyoung

To follow up on Vitaliy Ulantikov's solution I have supplemented it with a rename/move folder method:

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