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

Viewed 54197

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

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

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!

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.

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.

My solution for NET framework 3.5 and for NET framework version 4 and higher:

#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

Usage:

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