Como faço para excluir um diretório com arquivos somente leitura em c #?

Eu preciso excluir um diretório que contém arquivos somente leitura. Qual abordagem é melhor:

Com DirectoryInfo.Delete() , eu tenho que desativar manualmente o atributo somente leitura para cada arquivo, mas ManagementObject.InvokeMethod("Delete") não parece precisar. Existe alguma situação em que um é mais preferível ao outro?

Código de amostra (test.txt é somente leitura).

Primeira maneira:

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

Segunda maneira:

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

Aqui está um método de extensão que define Attributes como Normal recursivamente e, em seguida, exclui os itens:

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

A maneira mais simples de evitar chamadas recursivas é utilizando a opção AllDirectories ao obter FileSystemInfo , assim:

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

Tente isso

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

Outro método sem a necessidade de recursion.

 public static void ForceDeleteDirectory(string path) { DirectoryInfo root; Stack fols; DirectoryInfo fol; fols = new Stack(); 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(); } 

A melhor solução é marcar todos os arquivos como não-somente leitura e, em seguida, exclua o diretório.

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

Observe que ~ é um operador lógico bit a bit que retorna o complemento do valor binário fornecido. Eu não testei isso, mas deveria funcionar.

Obrigado!

Eu diria que sua primeira abordagem parece mais explícita e legível. O segundo método cheira a reflection, não é seguro e parece estranho. O ManagementObject pode representar várias coisas, portanto, não é óbvio que .InvokeMethod("Delete") realmente exclua um diretório.

A única coisa que eu não gosto sobre a primeira abordagem (directory.delete) é o caso em que existem subdiretórios que também contêm arquivos somente leitura, e eles têm subdiretórios que têm arquivos somente leitura, e assim por diante. Parece que você teria que desativar esse sinalizador para cada arquivo no diretório e em todos os subdiretórios de forma recursiva.

Com a segunda abordagem, você pode simplesmente excluir esse primeiro diretório e ele não verifica se os arquivos são somente leitura. No entanto, esta é a primeira vez que utilizo o WMI em C #, por isso não me sinto muito confortável com ele. Portanto, não tenho certeza de quando usar a abordagem WMI para outros aplicativos, em vez de apenas usar os methods System.IO.

Na superfície, usar a abordagem WMI parece ser mais eficiente do que iterar em todo o sistema de arquivos (suponha, por exemplo, que o diretório tenha dezenas de milhares de arquivos). Mas eu não sei que o WMI também não faz iterações. Se isso acontecer, estar mais perto do metal (novamente, suposições) deve ser mais eficiente.

Por elegância, admito que o método recursivo é legal.

O teste de desempenho deve responder à pergunta de eficiência. E também pode ser elegante se for incluído em um método de extensão de DirectoryInfo.

Aqui está outra solução que evita a recursion em si mesma.

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

Isso funciona pelos atributos resettings nas pastas e arquivos antes da exclusão, portanto, você pode simplesmente remover a última linha de um método ‘DirectoryResetAttributes’ e usar a exclusão separadamente.

Em uma nota relacionada, enquanto isso funcionava, tive problemas com a exclusão de caminhos que eram “muito longos” e acabei usando uma solução de robocopy postada aqui: C # excluindo uma pasta que possui caminhos longos

Para acompanhar a solução de Vitaliy Ulantikov, eu o adicionei com um método de renomear / mover pasta:

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