Question

Je dois supprimer un répertoire contenant des fichiers en lecture seule. Quelle approche est la meilleure:

  • Utilisation de DirectoryInfo.Delete () ou

  • ManagementObject.InvokeMethod (" Delete ") ?

Avec DirectoryInfo.Delete () , je dois désactiver manuellement l'attribut en lecture seule pour chaque fichier, mais ManagementObject.InvokeMethod (" suppression & # 160;) ne semble pas avoir besoin de. Existe-t-il une situation où l’une est préférable à l’autre?

Exemple de code (test.txt est en lecture seule).

Première manière:

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

Deuxième voie:

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)
    {
    }
}
Était-ce utile?

La solution

Voici une méthode d'extension qui définit Attributs sur Normal de manière récursive, puis supprime les éléments:

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

Autres conseils

Le moyen le plus simple d'éviter les appels récursifs consiste à utiliser l'option AllDirectories lors de l'obtention de FileSystemInfo , comme suit:

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

Essayez ceci,

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

Une autre méthode sans récursivité.

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 meilleure solution consiste à marquer tous les fichiers comme non lisibles, puis à supprimer le répertoire.

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

Notez que ~ est un opérateur logique bit à bit qui renvoie le complément de la valeur binaire donnée. Je n'ai pas testé cela, mais ça devrait marcher.

Merci!

Je dirais que votre première approche semble plus explicite et lisible. La deuxième méthode sent le reflet, n'est pas dactylographiée et a l'air bizarre. ManagementObject peut représenter plusieurs éléments. Il n'est donc pas évident que .InvokeMethod ("Supprimer") supprime réellement un répertoire.

La première chose que je n'aime pas dans la première approche (directory.delete) est le cas où il existe des sous-répertoires contenant également des fichiers en lecture seule et des sous-répertoires contenant également des fichiers en lecture seule. sur. Il semble que vous deviez désactiver cet indicateur pour chaque fichier du répertoire et tous les sous-répertoires de manière récursive.

Avec la seconde approche, vous pouvez simplement supprimer ce premier répertoire sans vérifier si les fichiers sont en lecture seule. Cependant, c'est la première fois que j'utilise WMI en C #, je ne suis donc pas à l'aise avec ça. Je ne sais donc pas quand utiliser l'approche WMI pour d'autres applications, au lieu d'utiliser simplement les méthodes System.IO.

En apparence, utiliser l’approche WMI semble plus efficace que d’itérer sur l’ensemble du système de fichiers (supposons par exemple que le répertoire contienne des dizaines de milliers de fichiers). Mais je ne sais pas non plus que WMI ne fait pas d'itérations. Si tel est le cas, étant plus proche du métal (encore une fois, les hypothèses), il devrait être plus efficace.

Pour l’élégance, j’admets que la méthode récursive est cool.

Les tests de performance devraient répondre à la question d'efficacité. Et l’un ou l’autre peut être élégant s’il est encapsulé dans une méthode d’extension de DirectoryInfo.

Voici une autre solution qui évite la récursion sur lui-même.

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

Cela fonctionne en réinitialisant les attributs des dossiers et des fichiers avant la suppression, vous pouvez donc simplement supprimer la dernière ligne de la méthode 'DirectoryResetAttributes' et utiliser la suppression séparément.

Sur une note connexe, pendant que cela fonctionnait, j’avais des problèmes avec la suppression des chemins "trop ??longs" et que j’avais fini par utiliser une solution robocopy publiée ici: C # suppression d'un dossier comportant de longs chemins

Pour faire suite à la solution de Vitaliy Ulantikov, je l’ai complétée par une méthode de changement de nom / déplacement de dossier:

  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();
  }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top