Jeśli próbujesz rekurencyjnie usunąć katalog, a
a katalog a\b
jest otwarty w Eksploratorze, b
zostanie usunięty, ale pojawi się błąd „katalog nie jest pusty”, a
mimo że jest pusty, gdy idziesz szukać. Bieżący katalog dowolnej aplikacji (w tym Eksploratora) zachowuje uchwyt do katalogu . Gdy zadzwonisz Directory.Delete(true)
, usuwa się od dołu do góry:, b
a następnie a
. Jeśli b
jest otwarty w Eksploratorze, Eksplorator wykryje usunięcie b
, zmieni katalog do góry cd ..
i wyczyści otwarte uchwyty. Ponieważ system plików działa asynchronicznie, plikDirectory.Delete
operacja kończy się niepowodzeniem z powodu konfliktu z Eksploratorem.
Niekompletne rozwiązanie
Pierwotnie opublikowałem następujące rozwiązanie, z myślą o przerwaniu bieżącego wątku, aby dać czas Eksploratorowi na zwolnienie uchwytu katalogu.
// incomplete!
try
{
Directory.Delete(path, true);
}
catch (IOException)
{
Thread.Sleep(0);
Directory.Delete(path, true);
}
Ale działa to tylko wtedy, gdy otwarty katalog jest bezpośrednim dzieckiem potomka usuwanego katalogu. Jeśli a\b\c\d
jest otwarty w Eksploratorze i używasz go a
, ta technika zawiedzie po usunięciu d
i c
.
Nieco lepsze rozwiązanie
Ta metoda obsłuży usuwanie głębokiej struktury katalogów, nawet jeśli jeden z katalogów niższego poziomu jest otwarty w Eksploratorze.
/// <summary>
/// Depth-first recursive delete, with handling for descendant
/// directories open in Windows Explorer.
/// </summary>
public static void DeleteDirectory(string path)
{
foreach (string directory in Directory.GetDirectories(path))
{
DeleteDirectory(directory);
}
try
{
Directory.Delete(path, true);
}
catch (IOException)
{
Directory.Delete(path, true);
}
catch (UnauthorizedAccessException)
{
Directory.Delete(path, true);
}
}
Pomimo dodatkowej pracy polegającej na samodzielnym wykonywaniu rekurencji nadal musimy sobie z tym poradzićUnauthorizedAccessException
które mogą wystąpić po drodze. Nie jest jasne, czy pierwsza próba usunięcia toruje drogę drugiej, udanej, czy tylko opóźnienie czasowe wprowadzone przez zgłoszenie wyjątku, który pozwala systemowi plików na nadrobienie zaległości.
Możesz być w stanie zmniejszyć liczbę wyjątków zgłaszanych i wychwytywanych w typowych warunkach, dodając a Thread.Sleep(0)
na początku try
bloku. Ponadto istnieje ryzyko, że pod dużym obciążeniem systemu możesz przelecieć przez obie Directory.Delete
próby i zawieść. Rozważ to rozwiązanie jako punkt wyjścia do bardziej niezawodnego usuwania rekurencyjnego.
Ogólna odpowiedź
To rozwiązanie dotyczy tylko osobliwości interakcji z Eksploratorem Windows. Jeśli chcesz mieć solidną operację usuwania, pamiętaj, że wszystko (skaner antywirusowy, cokolwiek) może mieć otwarty uchwyt do tego, co próbujesz usunąć w dowolnym momencie. Więc musisz spróbować ponownie później. To, ile później i ile razy spróbujesz, zależy od tego, jak ważne jest usunięcie obiektu. Jak wskazuje MSDN ,
Solidny kod iteracji plików musi uwzględniać wiele złożoności systemu plików.
To niewinne oświadczenie, zawierające jedynie link do dokumentacji referencyjnej NTFS, powinno sprawić, że twoje włosy staną na nogi.
( Edycja : Dużo. Ta odpowiedź pierwotnie miała tylko pierwsze, niepełne rozwiązanie).