Как удалить каталог с файлами только для чтения в C #? - PullRequest
53 голосов
/ 04 марта 2009

Мне нужно удалить каталог, содержащий файлы только для чтения. Какой подход лучше:

  • Использование 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)
    {
    }
}

Ответы [ 11 ]

92 голосов
/ 03 января 2012

Самый простой способ избежать рекурсивных вызовов - использовать опцию AllDirectories при получении FileSystemInfo s, например:

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);
}
87 голосов
/ 15 марта 2009

Вот метод расширения, который рекурсивно устанавливает Attributes в Normal, а затем удаляет элементы:

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();
}
9 голосов
/ 09 июня 2010

Попробуйте это,

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);
}
5 голосов
/ 21 ноября 2009

Еще один метод без необходимости рекурсии.

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);
}
3 голосов
/ 07 февраля 2011
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();
}
2 голосов
/ 05 марта 2009

Лучшее решение - пометить все файлы только для чтения, а затем удалить каталог.

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

Обратите внимание, что ~ - побитовый логический оператор, который возвращает дополнение заданного двоичного значения. Я не проверял это, но оно должно работать.

Спасибо!

1 голос
/ 06 марта 2009

Что мне не нравится в первом подходе (directory.delete), так это тот случай, когда есть подкаталоги, которые также содержат файлы только для чтения, и у них есть подкаталоги, которые также имеют файлы только для чтения, и т.д. на. Похоже, вам придется отключить этот флаг для каждого файла в каталоге и всех подкаталогов рекурсивно.

При втором подходе вы можете просто удалить этот первый каталог, и он не проверяет, доступны ли файлы только для чтения. Тем не менее, это первый раз, когда я использовал WMI в C #, поэтому мне не очень удобно с ним. Поэтому я не уверен, когда следует использовать подход WMI для других приложений, а не просто использовать методы System.IO.

1 голос
/ 05 марта 2009

Я бы сказал, что ваш первый подход выглядит более четким и читабельным. Второй метод пахнет отражением, небезопасен и выглядит странно. ManagementObject может представлять несколько вещей, поэтому не очевидно, что .InvokeMethod("Delete") фактически удаляет каталог.

0 голосов
/ 28 февраля 2013

Чтобы продолжить решение Виталия Улантикова, я дополнил его методом переименования / перемещения папок:

  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();
  }
0 голосов
/ 19 октября 2011

Вот еще одно решение, которое позволяет избежать рекурсии.

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» и использовать удаление отдельно.

В связанной заметке, хотя это работало, у меня возникли проблемы с удалением путей, которые были «слишком длинными» и закончились использованием решения для робокопии, опубликованного здесь: C # удаление папки с длинными путями

...