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

放肆的年华 提交于 2019-11-26 06:06:27

问题


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

  • Using DirectoryInfo.Delete(), or,

  • 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?

Sample code (test.txt is read only).

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

回答1:


Here is an extension method which sets Attributes to Normal recursively, then deletes the items:

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



回答2:


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

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



回答3:


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



回答4:


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



回答5:


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



回答6:


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!




回答7:


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 ManagementObject can represent multiple things, so it's not obvious that .InvokeMethod("Delete") actually deletes a directory.




回答8:


The thing that I don't like about the first approach (directory.delete) is the case where there are subdirectories that also contain read-only files, and they have subdirectories that have read-only files as well, and so on. It seems like you'd have to turn off that flag for every file in the directory and all subdirectories recursively.

With the second approach, you can just delete that first directory, and it doesn't check whether the files are read-only. However, this is the first time I've used WMI in C#, so I am not all that comfortable with it. So I am unsure when to go with the WMI approach for other applications, instead of just using the System.IO methods.




回答9:


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.

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.




回答10:


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.

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




回答11:


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

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


来源:https://stackoverflow.com/questions/611921/how-do-i-delete-a-directory-with-read-only-files-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!