Domanda

Devo eliminare una directory che contiene file di sola lettura. Quale approccio è migliore:

  • Uso di DirectoryInfo.Delete () o,

  • ManagementObject.InvokeMethod (" Cancella ") ?

Con DirectoryInfo.Delete () , devo disattivare manualmente l'attributo di sola lettura per ciascun file, ma ManagementObject.InvokeMethod (" Delete ") no sembra che sia necessario. Esiste una 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)
    {
    }
}
È stato utile?

Soluzione

Ecco un metodo di estensione che imposta Attributi su Normal in 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();
}

Altri suggerimenti

Il modo più semplice per evitare chiamate ricorsive è utilizzando 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 la necessità di ricorsione.

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);
}
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 di sola lettura, 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 Bitwise che restituisce il complemento del valore binario specificato. Non l'ho provato, ma dovrebbe funzionare.

Grazie!

Direi che il tuo primo approccio sembra più esplicito e leggibile. Il secondo metodo ha l'odore del riflesso, non è sicuro e sembra strano. ManagementObject può rappresentare più cose, quindi non è ovvio che .InvokeMethod (" Delete ") cancella 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ì sopra. Sembra che dovresti disattivare quel flag per ogni file nella directory e tutte le sottodirectory ricorsivamente.

Con il secondo approccio, puoi semplicemente 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 mi sento molto a mio agio con esso. Quindi non sono sicuro di quando utilizzare l'approccio WMI per altre applicazioni, invece di usare semplicemente i metodi System.IO.

In apparenza, l'uso dell'approccio WMI sembra più efficiente dell'iterazione sull'intero file system (supponiamo ad esempio che la directory abbia 10 di migliaia di file). Ma non so che anche WMI non esegua iterazioni. In tal caso, essendo più vicino al metallo (di nuovo, ipotesi) dovrebbe essere più efficiente.

Per eleganza, ammetto che il metodo ricorsivo è interessante.

I test delle prestazioni dovrebbero rispondere alla domanda di efficienza. E può essere elegante se racchiuso 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 reimpostando gli attributi sulle cartelle e sui file prima dell'eliminazione, quindi puoi semplicemente rimuovere l'ultima riga per un metodo "DirectoryResetAttributes" e usare l'eliminazione separatamente.

In una nota correlata, mentre funzionava, ho avuto problemi con l'eliminazione di percorsi "troppo lunghi" e ho finito per usare una soluzione di robocopy pubblicata qui: C # elimina una cartella che ha percorsi lunghi

Per dare seguito alla soluzione di Vitaliy Ulantikov, l'ho integrata con un metodo per rinominare / spostare le 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();
  }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top