C#で読み取り専用ファイルのあるディレクトリを削除するにはどうすればよいですか?
-
03-07-2019 - |
質問
読み取り専用ファイルを含むディレクトリを削除する必要があります。どちらのアプローチが良いか:
-
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)
{
}
}
解決
これは、 Attributes
を Normal
に再帰的に設定し、アイテムを削除する拡張メソッドです。
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();
}
他のヒント
再帰呼び出しを回避する最も簡単な方法は、 FileSystemInfo
を取得するときに AllDirectories
オプションを利用することです。
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);
}
これを試してください
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);
}
再帰を必要としない別の方法。
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();
}
最善の解決策は、すべてのファイルを非読み取り専用としてマークし、ディレクトリを削除することです。
// 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番目の方法は、リフレクションのような匂いがし、タイプセーフではなく、奇妙に見えます。 ManagementObject
は複数のものを表すことができるため、 .InvokeMethod(&quot; Delete&quot;)
が実際にディレクトリを削除することは明らかではありません。
最初のアプローチ(directory.delete)で気に入らないのは、読み取り専用ファイルも含むサブディレクトリがあり、読み取り専用ファイルもあるサブディレクトリがある場合です。に。ディレクトリ内のすべてのファイルとすべてのサブディレクトリについて、このフラグを再帰的にオフにする必要があるようです。
2番目のアプローチでは、最初のディレクトリを削除するだけで、ファイルが読み取り専用かどうかはチェックされません。ただし、C#でWMIを使用したのはこれが初めてであるため、私はこれにそれほど満足していません。そのため、System.IOメソッドを使用するだけでなく、他のアプリケーションのWMIアプローチをいつ採用すべきかわかりません。
表面的には、WMIアプローチを使用すると、ファイルシステム全体を反復処理するよりも効率的です(たとえば、ディレクトリに何万ものファイルがあると仮定します)。しかし、WMIが繰り返しを実行しないことも知りません。もしそうなら、金属に近いほうが(再び仮定)、より効率的であるはずです。
エレガントさのために、再帰的な方法はクールだと思います。
パフォーマンステストは、効率の質問に答える必要があります。また、DirectoryInfoの拡張メソッドでラップすると、どちらもエレガントになります。
これ自体での再帰を回避する別のソリューションがあります。
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」メソッドの最後の行を削除して、個別に削除を使用できます。
関連するメモでは、これは機能していましたが、「長すぎる」パスを削除する際に問題が発生し、ロボコピーソリューションを使用してしまいました: C#長いパスを持つフォルダーの削除
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();
}