Come si elimina una directory con file di sola lettura in C #?

Ho bisogno di cancellare una directory che contiene file di sola lettura. Quale approccio è meglio:

Con DirectoryInfo.Delete() , devo distriggersre manualmente l’attributo di sola lettura per ogni file, ma non è necessario che ManagementObject.InvokeMethod("Delete") appaia. C’è qualche situazione in cui uno è più preferibile all’altro?

Codice di esempio (test.txt è di sola lettura).

Primo modo:

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

Secondo modo:

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

Ecco un metodo di estensione che imposta gli Attributes su Normal modo ricorsivo, quindi elimina gli elementi:

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

Il modo più semplice per evitare le chiamate ricorsive consiste nell’utilizzare l’opzione AllDirectories quando si ottengono FileSystemInfo , in questo modo:

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

Prova questo,

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

Un altro metodo senza necessità di ricorsione.

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

La soluzione migliore è contrassegnare tutti i file come non letti, quindi eliminare la 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)); 

Si noti che ~ è un operatore logico bit a bit che restituisce il complemento del valore binario specificato. Non ho provato questo, ma dovrebbe funzionare.

Grazie!

Direi che il tuo primo approccio sembra più esplicito e leggibile. Il secondo metodo odora di riflessione, non è sicuro e sembra strano. ManagementObject può rappresentare più cose, quindi non è ovvio che .InvokeMethod("Delete") effettivamente una directory.

La cosa che non mi piace del primo approccio (directory.delete) è il caso in cui ci sono sottodirectory che contengono anche file di sola lettura, e hanno sottodirectory che hanno anche file di sola lettura, e così via. Sembra che tu debba distriggersre quel flag per ogni file nella directory e tutte le sottodirectory in modo ricorsivo.

Con il secondo approccio, è sufficiente eliminare quella prima directory e non controlla se i file sono di sola lettura. Tuttavia, questa è la prima volta che utilizzo WMI in C #, quindi non sono così comodo con esso. Quindi non sono sicuro quando utilizzare l’approccio WMI per altre applicazioni, invece di utilizzare solo i metodi System.IO.

In superficie, l’utilizzo dell’approccio WMI sembra più efficiente dell’iterazione sull’intero file system (supponiamo ad esempio che la directory abbia decine di migliaia di file). Ma non so che anche WMI non faccia iterazioni. Se lo fa, essendo più vicino al metallo (di nuovo, ipotesi) dovrebbe essere più efficiente.

Per eleganza, ammetto che il metodo ricorsivo è fantastico.

Il test delle prestazioni dovrebbe rispondere alla domanda sull’efficienza. E uno può essere elegante se avvolto in un metodo di estensione di DirectoryInfo.

Ecco un’altra soluzione che evita la ricorsione su se stessa.

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

Funziona in base agli attributi di reimpostazione delle cartelle e dei file prima dell’eliminazione, pertanto è sufficiente rimuovere l’ultima riga per un metodo “DirectoryResetAttributes” e utilizzare delete separatamente.

Su una nota correlata, mentre questo ha funzionato, ho avuto problemi con l’eliminazione di percorsi che erano “troppo lunghi” e ho finito con l’utilizzo di una soluzione di robocopy pubblicata qui: C # eliminando una cartella che ha percorsi lunghi

Per dare seguito alla soluzione di Vitaliy Ulantikov, l’ho completata con un metodo di ridenominazione / spostamento delle cartelle:

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