web-dev-qa-db-ja.com

C#で読み取り専用ファイルのあるディレクトリを削除するにはどうすればよいですか?

読み取り専用ファイルを含むディレクトリを削除する必要があります。どちらのアプローチが良いですか:

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

2番目の方法:

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)
    {
    }
}
54
David Hodgson

以下は、AttributesNormalに再帰的に設定し、アイテムを削除する拡張メソッドです。

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

再帰呼び出しを回避する最も簡単な方法は、次のようにAllDirectoriessを取得するときにFileSystemInfoオプションを使用することです。

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

これを試して、

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);
}
9
Frederic Arteau

再帰を必要としない別の方法。

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);
}
5
jasonk
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();
}
3
jdvatwork

最善の解決策は、すべてのファイルを非読み取り専用としてマークし、ディレクトリを削除することです。

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

〜は、指定されたバイナリ値の補数を返すビット単位の論理演算子です。これはテストしていませんが、動作するはずです。

ありがとう!

2
Socratees

最初のアプローチ(directory.delete)で気に入らないのは、読み取り専用ファイルも含むサブディレクトリがあり、読み取り専用ファイルも含むサブディレクトリがある場合などです。ディレクトリ内のすべてのファイルとすべてのサブディレクトリに対して、このフラグを再帰的にオフにする必要があるようです。

2番目のアプローチでは、最初のディレクトリを削除するだけで、ファイルが読み取り専用かどうかはチェックされません。ただし、C#でWMIを使用したのはこれが初めてなので、これにそれほど慣れていません。そのため、System.IOメソッドを使用するだけでなく、他のアプリケーションでWMIアプローチをいつ使用するかはわかりません。

1
David Hodgson

あなたの最初のアプローチは、より明確で読みやすいように見えます。 2番目の方法は、リフレクションのような匂いがし、タイプセーフではなく、奇妙に見えます。 ManagementObjectは複数のものを表すことができるため、.InvokeMethod("Delete")が実際にディレクトリを削除することは明らかではありません。

1
Thomas Eyde

表面的には、WMIアプローチを使用すると、ファイルシステム全体を反復処理するよりも効率的と思われます(たとえば、ディレクトリに何万ものファイルがあると仮定します)。しかし、WMIが反復を行わないことも知りません。もしそうなら、金属に近くなると(再び仮定)、より効率的になるはずです。

優雅さのために、再帰的な方法はクールだと思います。

パフォーマンステストは、効率の質問に答える必要があります。また、DirectoryInfoの拡張メソッドでラップすると、どちらもエレガントになります。

0
Donald Byrd

Vitaliy Ulantikovのソリューションをフォローアップするために、フォルダ名の変更/移動方法を追加しました。

  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
cdavidyoung

再帰を回避する別のソリューションを次に示します。

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」メソッドの最後の行を削除して、deleteを個別に使用できます。

関連するメモでは、これは機能していましたが、「長すぎる」パスを削除するときに問題が発生し、ここに投稿されたロボコピーソリューションを使用することになりました: C#長いパスを持つフォルダーを削除します

0
WhoIsRich