Nie można usunąć katalogu za pomocą Directory.Delete (ścieżka, prawda)

383

Korzystam z .NET 3.5, próbując rekurencyjnie usunąć katalog używając:

Directory.Delete(myPath, true);

Rozumiem, że powinno to wyrzucić, jeśli pliki są w użyciu lub występuje problem z uprawnieniami, ale w przeciwnym razie powinien usunąć katalog i całą jego zawartość.

Jednak czasami otrzymuję to:

System.IO.IOException: The directory is not empty.
    at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
    at System.IO.Directory.DeleteHelper(String fullPath, String userPath, Boolean recursive)
    at System.IO.Directory.Delete(String fullPath, String userPath, Boolean recursive)
    ...

Nie dziwię się, że metoda czasami rzuca, ale jestem zaskoczony otrzymaniem tego konkretnego komunikatu, gdy rekurencja jest prawdziwa. ( Wiem, że katalog nie jest pusty.)

Czy istnieje powód, dla którego widziałbym to zamiast AccessViolationException?

Jason Anderson
źródło
13
Nie zobaczysz AccessViolationException - dotyczy to nieprawidłowych operacji wskaźnika, a nie dostępu do dysku.
Joe White
1
Wydaje się, że jest to jakiś problem z IO inny niż brak pustego katalogu, na przykład otwarte uchwyty plików lub coś takiego. Spróbowałbym użyć opcji usuwania rekurencyjnego, a następnie w celu wykrycia wyjątku IOException wyszukać i zamknąć wszystkie otwarte uchwyty plików, a następnie spróbować ponownie. Dyskusja na ten temat tutaj: stackoverflow.com/questions/177146/...
Dan Csharpster

Odpowiedzi:

231

Uwaga edytora: Mimo że ta odpowiedź zawiera przydatne informacje, jest nieprawidłowa pod względem działania Directory.Delete. Przeczytaj komentarze do tej odpowiedzi i inne odpowiedzi na to pytanie.


Wcześniej napotkałem ten problem.

Przyczyną problemu jest to, że ta funkcja nie usuwa plików znajdujących się w strukturze katalogów. Musisz więc stworzyć funkcję, która usuwa wszystkie pliki w strukturze katalogów, a następnie wszystkie katalogi przed usunięciem samego katalogu. Wiem, że jest to sprzeczne z drugim parametrem, ale jest to znacznie bezpieczniejsze podejście. Ponadto prawdopodobnie będziesz chciał usunąć atrybuty dostępu TYLKO DO CZYTANIA z plików bezpośrednio przed ich usunięciem. W przeciwnym razie powstanie wyjątek.

Po prostu włóż ten kod do swojego projektu.

public static void DeleteDirectory(string target_dir)
{
    string[] files = Directory.GetFiles(target_dir);
    string[] dirs = Directory.GetDirectories(target_dir);

    foreach (string file in files)
    {
        File.SetAttributes(file, FileAttributes.Normal);
        File.Delete(file);
    }

    foreach (string dir in dirs)
    {
        DeleteDirectory(dir);
    }

    Directory.Delete(target_dir, false);
}

Również dla mnie osobiście dodaję ograniczenie w obszarach komputera, które można usunąć, ponieważ chcesz, aby ktoś wywoływał tę funkcję na C:\WINDOWS (%WinDir%)lub C:\.

Jeremy Edwards
źródło
117
To nie ma sensu. Directory.Delete (myPath, true) to przeciążenie, które usuwa wszystkie pliki znajdujące się w strukturze katalogów. Jeśli chcesz się pomylić, pomyl się z odpowiedzią Ryana S.
Sig. Tolleranza
35
+1, ponieważ chociaż Directory.Delete () usuwa pliki ze swoich podkatalogów (z recursive = true), generuje „wyjątek IO: katalog nie jest pusty”, jeśli jeden z podkatalogów lub plików jest tylko do odczytu. To rozwiązanie działa lepiej niż Directory.Delete ()
Anthony Brien
17
Twoje oświadczenie, które Directory.Delete(path, true)nie usuwa plików, jest nieprawidłowe. Zobacz MSDN msdn.microsoft.com/en-us/library/fxeahc5f.aspx
Konstantin Spirin
20
-1 Czy ktoś może postawić wyraźny znacznik, że ważność tego podejścia jest bardzo wątpliwa. Jeśli Directory.Delete(string,bool)zawiedzie, coś jest zablokowane lub niewłaściwie dopuszczone i nie ma jednego rozmiaru pasującego do wszystkich rozwiązań takiego problemu. Ludzie muszą rozwiązać ten problem w swoim kontekście, a my nie powinniśmy robić wielkiego włochatego rzutu każdym pomysłem na problem (z ponownymi próbami i połykaniem wyjątków) i mieć nadzieję na dobry wynik.
Ruben Bartelink
37
Uważaj na to podejście, jeśli usuwany katalog zawiera skróty / dowiązania symboliczne do innych folderów - możesz skończyć usuwanie więcej, niż się spodziewałeś
Chanakya
182

Jeśli próbujesz rekurencyjnie usunąć katalog, aa katalog a\bjest otwarty w Eksploratorze, bzostanie usunięty, ale pojawi się błąd „katalog nie jest pusty”, amimo ż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:, ba następnie a. Jeśli bjest 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\djest otwarty w Eksploratorze i używasz go a, ta technika zawiedzie po usunięciu di 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 trybloku. Ponadto istnieje ryzyko, że pod dużym obciążeniem systemu możesz przelecieć przez obie Directory.Deletepró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).

ryscl
źródło
11
Wygląda na to, że wywołuje Directory.Delete (ścieżka, prawda), gdy ścieżka lub jeden z folderów / plików pod ścieżką jest otwarty lub wybrany w Eksploratorze Windows, zgłosi wyjątek IOException. Zamykanie Eksploratora Windows i ponowne uruchamianie mojego istniejącego kodu bez sugerowanego powyżej try / catch działało poprawnie.
David Alpert
1
Nie mogę pojąć, jak i dlaczego to działa, ale zadziałało to dla mnie podczas ustawiania atrybutów pliku i pisania mojej własnej funkcji rekurencyjnej.
Stilgar,
1
@CllosLiu Ponieważ daje „Eksploratorowi szansę na zwolnienie uchwytu katalogu”
Dmitry Gonchar
4
Dzieje się tak, że system prosi Eksploratora o „zwolnienie uchwytu katalogu”, a następnie próbuje usunąć katalog. Jeśli uchwyt katalogu nie został usunięty na czas, zgłaszany jest wyjątek i catchblok jest wykonywany (tymczasem Explorer nadal zwalnia katalog, ponieważ nie wysłano żadnego polecenia informującego go, aby tego nie robił). Wezwanie do Thread.Sleep(0)może, ale nie musi być konieczne, ponieważ catchblok dał już systemowi trochę więcej czasu, ale zapewnia dodatkowe bezpieczeństwo za niewielką cenę. Następnie Deletejest wywoływany, a katalog jest już zwolniony.
Zachary Kniebel
1
@ PandaWood faktycznie działał tylko ten Sleep (100). Sen (0) nie działał. Nie mam pojęcia, co się dzieje i jak to rozwiązać poprawnie. Mam na myśli, co jeśli to zależy od obciążenia serwera, a w przyszłości powinno być 300 lub 400? Jak to wiedzieć. Musi to być inna właściwa droga ...
Roman
43

Zanim przejdziesz dalej, sprawdź następujące przyczyny, które są pod twoją kontrolą:

  • Czy folder jest ustawiony jako bieżący katalog Twojego procesu? Jeśli tak, zmień to najpierw na coś innego.
  • Czy otworzyłeś plik (lub załadowałeś bibliotekę DLL) z tego folderu? (i zapomniałem go zamknąć / rozładować)

W przeciwnym razie sprawdź następujące uzasadnione powody, na które nie masz wpływu:

  • W tym folderze znajdują się pliki oznaczone jako tylko do odczytu.
  • Nie masz uprawnień do usuwania niektórych z tych plików.
  • Plik lub podfolder jest otwarty w Eksploratorze lub innej aplikacji.

Jeśli którykolwiek z powyższych problemów stanowi problem, powinieneś zrozumieć, dlaczego tak się dzieje, zanim spróbujesz poprawić kod usuwania. Czy Twoja aplikacja powinna usuwać pliki tylko do odczytu lub niedostępne? Kto tak je oznaczył i dlaczego?

Po wykluczeniu powyższych przyczyn nadal istnieje możliwość fałszywych awarii. Usunięcie nie powiedzie się, jeśli ktoś będzie trzymał uchwyt usuwanego pliku lub folderu, a istnieje wiele powodów, dla których ktoś może wyliczyć folder lub odczytać jego pliki:

  • indeksuj wyszukiwanie
  • antywirusy
  • oprogramowanie do tworzenia kopii zapasowych

Ogólne podejście do radzenia sobie z fałszywymi niepowodzeniami polega na wielokrotnym próbowaniu, przerywaniu między próbami. Oczywiście nie chcesz próbować wiecznie, więc powinieneś poddać się po pewnej liczbie prób i albo rzucić wyjątek, albo zignorować błąd. Lubię to:

private static void DeleteRecursivelyWithMagicDust(string destinationDir) {
    const int magicDust = 10;
    for (var gnomes = 1; gnomes <= magicDust; gnomes++) {
        try {
            Directory.Delete(destinationDir, true);
        } catch (DirectoryNotFoundException) {
            return;  // good!
        } catch (IOException) { // System.IO.IOException: The directory is not empty
            System.Diagnostics.Debug.WriteLine("Gnomes prevent deletion of {0}! Applying magic dust, attempt #{1}.", destinationDir, gnomes);

            // see http://stackoverflow.com/questions/329355/cannot-delete-directory-with-directory-deletepath-true for more magic
            Thread.Sleep(50);
            continue;
        }
        return;
    }
    // depending on your use case, consider throwing an exception here
}

Moim zdaniem takiego pomocnika należy używać do wszystkich operacji usuwania, ponieważ fałszywe niepowodzenia są zawsze możliwe. NALEŻY DOSTOSOWAĆ TEN KOD DO SWOJEJ PRZYPADKU, a nie tylko na ślepo kopiować.

Miałem fałszywe awarie wewnętrznego folderu danych generowanego przez moją aplikację, zlokalizowanego pod% LocalAppData%, więc moja analiza wygląda następująco:

  1. Folder jest kontrolowany wyłącznie przez moją aplikację, a użytkownik nie ma uzasadnionego powodu, aby oznaczać rzeczy jako tylko do odczytu lub niedostępne w tym folderze, więc nie próbuję obsługiwać tej sprawy.

  2. Nie ma tam żadnych cennych rzeczy stworzonych przez użytkowników, więc nie ma ryzyka wymuszonego usunięcia czegoś przez pomyłkę.

  3. Będąc wewnętrznym folderem danych, nie oczekuję, że będzie on otwarty w Eksploratorze, a przynajmniej nie czuję potrzeby szczegółowej obsługi sprawy (tj. Dobrze sobie radzę z tą sprawą poprzez wsparcie).

  4. Jeśli wszystkie próby zakończą się niepowodzeniem, zdecyduję się zignorować błąd. W najgorszym przypadku aplikacja nie rozpakowuje niektórych nowszych zasobów, ulega awarii i monituje użytkownika o kontakt z pomocą techniczną, co jest dla mnie dopuszczalne, o ile nie zdarza się to często. Lub, jeśli aplikacja się nie zawiesi, pozostawi po sobie trochę starych danych, co znowu jest dla mnie akceptowalne.

  5. Wybieram ograniczenie ponownych prób do 500ms (50 * 10). Jest to arbitralny próg, który działa w praktyce; Chciałem, aby próg był wystarczająco krótki, aby użytkownicy nie zabili aplikacji, sądząc, że przestała ona odpowiadać. Z drugiej strony, pół sekundy to dużo czasu dla sprawcy na zakończenie przetwarzania mojego folderu. Sądząc po innych odpowiedziach SO, które czasami wydają się nawet Sleep(0)do zaakceptowania, bardzo niewielu użytkowników kiedykolwiek doświadczy więcej niż jednej próby.

  6. Próbuję co 50 ms, co jest kolejną dowolną liczbą. Wydaje mi się, że jeśli plik jest przetwarzany (indeksowany, sprawdzany), gdy próbuję go usunąć, 50 ms to właściwy czas, aby oczekiwać zakończenia przetwarzania w moim przypadku. Ponadto 50 ms jest wystarczająco małe, aby nie spowodować zauważalnego spowolnienia; znowu Sleep(0)wydaje się wystarczające w wielu przypadkach, więc nie chcemy zbytnio opóźniać.

  7. Kod próbuje powtórzyć wyjątki we / wy. Zwykle nie oczekuję żadnych wyjątków w dostępie do% LocalAppData%, więc wybrałem prostotę i zaakceptowałem ryzyko 500 ms opóźnienia w przypadku wystąpienia uzasadnionego wyjątku. Nie chciałem też znaleźć sposobu na wykrycie dokładnego wyjątku, na którym chcę spróbować ponownie.

Andrey Tarantsov
źródło
7
PPS Kilka miesięcy później z przyjemnością informuję, że ten (nieco szalony) fragment kodu całkowicie rozwiązał problem. Liczba zgłoszeń do pomocy technicznej dotyczących tego problemu spadła do zera (z około 1-2 tygodniowo).
Andriej Tarantow
1
+0 Chociaż jest to bardziej niezawodne i mniej „tutaj jest; idealne rozwiązanie dla ciebie ”niż stackoverflow.com/a/7518831/11635 , dla mnie to samo dotyczy - programowanie przez przypadek - obchodź się ostrożnie. Jednym z użytecznych punktów zawartych w twoim kodzie jest to, że jeśli zamierzasz spróbować ponownie, musisz wziąć pod uwagę, że jesteś w wyścigu z dwuznacznością, czy Directory zniknął od ostatniej próby [a nowy Directory.Existsstrażnik by to zrobił nie rozwiązuj tego.]
Ruben Bartelink
1
uwielbiam to ... nie wiem, co robię, to zawsze jest dla mnie bolesne ... ale nie dlatego, że mam otwarty katalog w eksploratorze ... niewiele szumu w internecie o tym więcej -lub-mniej błędów ... przynajmniej ja i Andrey mamy sposób sobie z tym poradzić :)
TCC
2
@ RubenBartelink OK, więc myślę, że możemy się z tym zgodzić: opublikowanie fragmentu kodu, który działa dla jednej konkretnej aplikacji (i nigdy nie miał być odpowiedni dla każdego przypadku), ponieważ odpowiedź SO będzie przeszkodą dla wielu początkujących i / lub ignorantów programistów. Dałem to jako punkt wyjścia do personalizacji, ale tak, niektórzy ludzie będą go używać w obecnej postaci, a to źle.
Andrey Tarantsov
2
@nopara Nie potrzebujesz porównania; jeśli nie jesteśmy w pętli, ponieśliśmy porażkę. I tak, w wielu przypadkach będziesz chciał zgłosić wyjątek, a następnie dodać odpowiedni kod obsługi błędów na stosie, prawdopodobnie z komunikatem widocznym dla użytkownika.
Andriej Tarantow
18

Nowoczesna odpowiedź asynchroniczna

Przyjęta odpowiedź jest po prostu błędna, może działać dla niektórych osób, ponieważ czas potrzebny na pobranie plików z dysku uwalnia wszystko, co je blokowało. Faktem jest, że dzieje się tak, ponieważ pliki są blokowane przez inny proces / strumień / akcję. Inne odpowiedzi używają Thread.Sleep(Fuj), aby ponownie spróbować usunąć katalog po pewnym czasie. To pytanie wymaga ponownej odpowiedzi z bardziej nowoczesną odpowiedzią.

public static async Task<bool> TryDeleteDirectory(
   string directoryPath,
   int maxRetries = 10,
   int millisecondsDelay = 30)
{
    if (directoryPath == null)
        throw new ArgumentNullException(directoryPath);
    if (maxRetries < 1)
        throw new ArgumentOutOfRangeException(nameof(maxRetries));
    if (millisecondsDelay < 1)
        throw new ArgumentOutOfRangeException(nameof(millisecondsDelay));

    for (int i = 0; i < maxRetries; ++i)
    {
        try
        {
            if (Directory.Exists(directoryPath))
            {
                Directory.Delete(directoryPath, true);
            }

            return true;
        }
        catch (IOException)
        {
            await Task.Delay(millisecondsDelay);
        }
        catch (UnauthorizedAccessException)
        {
            await Task.Delay(millisecondsDelay);
        }
    }

    return false;
}

Testy jednostkowe

Testy te pokazują przykład, w jaki sposób zablokowany plik może spowodować Directory.Deleteawarię i jak TryDeleteDirectorypowyższa metoda rozwiązuje problem.

[Fact]
public async Task TryDeleteDirectory_FileLocked_DirectoryNotDeletedReturnsFalse()
{
    var directoryPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
    var subDirectoryPath = Path.Combine(Path.GetTempPath(), "SubDirectory");
    var filePath = Path.Combine(directoryPath, "File.txt");

    try
    {
        Directory.CreateDirectory(directoryPath);
        Directory.CreateDirectory(subDirectoryPath);

        using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Write))
        {
            var result = await TryDeleteDirectory(directoryPath, 3, 30);
            Assert.False(result);
            Assert.True(Directory.Exists(directoryPath));
        }
    }
    finally
    {
        if (Directory.Exists(directoryPath))
        {
            Directory.Delete(directoryPath, true);
        }
    }
}

[Fact]
public async Task TryDeleteDirectory_FileLockedThenReleased_DirectoryDeletedReturnsTrue()
{
    var directoryPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
    var subDirectoryPath = Path.Combine(Path.GetTempPath(), "SubDirectory");
    var filePath = Path.Combine(directoryPath, "File.txt");

    try
    {
        Directory.CreateDirectory(directoryPath);
        Directory.CreateDirectory(subDirectoryPath);

        Task<bool> task;
        using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Write))
        {
            task = TryDeleteDirectory(directoryPath, 3, 30);
            await Task.Delay(30);
            Assert.True(Directory.Exists(directoryPath));
        }

        var result = await task;
        Assert.True(result);
        Assert.False(Directory.Exists(directoryPath));
    }
    finally
    {
        if (Directory.Exists(directoryPath))
        {
            Directory.Delete(directoryPath, true);
        }
    }
}
Muhammad Rehan Saeed
źródło
Czy możesz rozwinąć to, co rozumiesz przez „nowoczesny”? Jakie są zalety twojego podejścia? Dlaczego inni, twoim zdaniem, są w błędzie?
TinyRacoon
1
Inni się nie mylą. Po prostu używają starszych interfejsów API, Thread.Sleepktórych powinieneś dziś unikać i zamiast tego używaj async/ awaitwith Task.Delay. To zrozumiałe, to bardzo stare pytanie.
Muhammad Rehan Saeed,
To podejście nie działa w VB.Net (przynajmniej nie z bardzo dosłowną konwersją linia-linia) z powoduBC36943 'Await' cannot be used inside a 'Catch' statement, a 'Finally' statement, or a 'SyncLock' statement.
amonroejj
@amonroejj Musisz używać starszej wersji. To zostało naprawione.
Muhammad Rehan Saeed,
Mała poprawka zamiast zwracać true, if (!Directory.Exists(directoryPath)) { return true; } await Task.Delay(millisecondsDelay); aby poczekać, aż katalog naprawdę zniknie
fuchs777
16

Jedną ważną rzeczą, o której należy wspomnieć (dodałem to jako komentarz, ale nie wolno mi), że zachowanie przeciążenia zmieniło się z .NET 3.5 na .NET 4.0.

Directory.Delete(myPath, true);

Począwszy od .NET 4.0 usuwa pliki w samym folderze, ale NIE w 3.5. Można to również zobaczyć w dokumentacji MSDN.

.NET 4.0

Usuwa określony katalog oraz, jeśli jest to wskazane, wszystkie podkatalogi i pliki w katalogu.

.NET 3.5

Usuwa pusty katalog i, jeśli jest to wskazane, wszystkie podkatalogi i pliki w katalogu.

jettatore
źródło
3
Myślę, że to tylko zmiana dokumentacji ... jeśli usunie tylko „pusty katalog”, co oznaczałoby usunięcie również plików w katalogu z parametrem 2 °? Jeśli jest pusty, nie ma żadnych plików ...
Pisu
Obawiam się, że nie rozumiesz. Opublikowałem to po przetestowaniu kodu w obu wersjach frameworka. Usunięcie niepustego folderu w wersji 3.5 spowoduje zgłoszenie wyjątku.
jettatore
15

Miałem ten sam problem za Delphi. W rezultacie moja aplikacja blokowała katalog, który chciałem usunąć. Jakoś katalog został zablokowany, kiedy do niego pisałem (niektóre pliki tymczasowe).

Złap 22 polegał na tym, że przed usunięciem zrobiłem prosty katalog zmian do jego rodzica.

Drejc
źródło
6
+1 Teraz jest coś, o czym msdn dla Directory.Delete wspomina!
Ruben Bartelink,
3
jakieś ostateczne rozwiązanie z pełną próbką kodu źródłowego?
Kiquenet
11

Dziwi mnie, że nikt nie pomyślał o tej prostej nierekurencyjnej metodzie, która może usuwać katalogi zawierające pliki tylko do odczytu, bez potrzeby zmiany atrybutu tylko do odczytu każdego z nich.

Process.Start("cmd.exe", "/c " + @"rmdir /s/q C:\Test\TestDirectoryContainingReadOnlyFiles"); 

(Zmień trochę, aby nie uruchamiać na chwilę okna cmd, które jest dostępne w całym Internecie)

Piyush Soni
źródło
Miło się z nami podzielić, ale czy byłbyś tak uprzejmy, aby uwzględnić odrobinę zmian potrzebnych, aby zapobiec uruchomieniu okna cmd, zamiast zachęcać nas do szukania go przez sieć?
ThunderGr
To nie działa W tej samej sytuacji, w której mogę usunąć plik z wiersza polecenia lub Eksploratora, użycie tego kodu do wywołania rmdir daje kod wyjścia 145, który tłumaczy się na „Katalog nie jest pusty”. Pozostawia katalog pusty, ale nadal na swoim miejscu, dokładnie tak jak Directory.Delete („”, true)
Kevin Coulombe
@Kevin Coulombe, Humm ... Czy na pewno używasz przełączników / s / q?
Piyush Soni,
1
@KevinCoulombe: Tak, muszą to być te elementy COM. Kiedy próbuję przez zwykły stary C #, działa i usuwa katalog wraz z plikami wewnątrz (tylko do odczytu lub tylko do odczytu).
Piyush Soni
5
Jeśli zaczniesz polegać na zewnętrznych komponentach, które powinny znajdować się w frameworku, jest to pomysł „mniej niż idealny”, ponieważ nie jest już przenośny (lub trudniejszy). Co jeśli exe tam nie ma? Czy zmieniono opcję /? Jeśli rozwiązanie Jeremy Edwards działa, to powinno być preferowane IMHO
francuski
11

Możesz odtworzyć błąd, uruchamiając:

Directory.CreateDirectory(@"C:\Temp\a\b\c\");
Process.Start(@"C:\Temp\a\b\c\");
Thread.Sleep(1000);
Directory.Delete(@"C:\Temp\a\b\c");
Directory.Delete(@"C:\Temp\a\b");
Directory.Delete(@"C:\Temp\a");

Podczas próby usunięcia katalogu „b” zgłasza wyjątek IOException „Katalog nie jest pusty”. To głupie, ponieważ właśnie usunęliśmy katalog „c”.

Z mojego zrozumienia wynika, że ​​katalog „c” jest stemplowany jako usunięty. Ale usunięcie nie jest jeszcze zatwierdzone w systemie. System ma odpowiedź, że zadanie zostało wykonane, podczas gdy w rzeczywistości jest nadal przetwarzane. System prawdopodobnie czeka, aż eksplorator plików skupi się na katalogu nadrzędnym, aby zatwierdzić usunięcie.

Jeśli spojrzysz na kod źródłowy funkcji Delete ( http://referencesource.microsoft.com/#mscorlib/system/io/directory.cs ), zobaczysz, że używa natywnej funkcji Win32Native.RemoveDirectory. Zwrócono uwagę na to, że nie trzeba czekać:

Funkcja RemoveDirectory oznacza katalog do usunięcia po zamknięciu. Dlatego katalog nie jest usuwany, dopóki ostatni uchwyt do katalogu nie zostanie zamknięty.

( http://msdn.microsoft.com/en-us/library/windows/desktop/aa365488(v=vs.85).aspx )

Uśpij i spróbuj ponownie. Por. Rozwiązanie ryascl.

Olivier de Rivoyre
źródło
8

Miałem te dziwne problemy z uprawnieniami podczas usuwania katalogów profilu użytkownika (w C: \ Documents and Settings), mimo że mogłem to zrobić w powłoce Eksploratora.

File.SetAttributes(target_dir, FileAttributes.Normal);
Directory.Delete(target_dir, false);

Nie ma dla mnie sensu, co robi operacja „pliku” na katalogu, ale wiem, że to działa i to mi wystarczy!

p.campbell
źródło
2
Nadal nie ma nadziei, gdy katalog ma wiele plików, a Eksplorator otwiera folder zawierający te pliki.
widzi
3

Ta odpowiedź jest oparta na: https://stackoverflow.com/a/1703799/184528 . Różnica w stosunku do mojego kodu polega na tym, że rekursywne usuwanie wielu podkatalogów i plików następuje tylko wtedy, gdy jest to konieczne.

    public static void DeleteDirectory(string dir, bool secondAttempt = false)
    {
        // If this is a second try, we are going to manually 
        // delete the files and sub-directories. 
        if (secondAttempt)
        {
            // Interrupt the current thread to allow Explorer time to release a directory handle
            Thread.Sleep(0);

            // Delete any files in the directory 
            foreach (var f in Directory.GetFiles(dir, "*.*", SearchOption.TopDirectoryOnly))
                File.Delete(f);

            // Try manually recursing and deleting sub-directories 
            foreach (var d in Directory.GetDirectories(dir))
                DeleteDirectory(d);

            // Now we try to delete the current directory
            Directory.Delete(dir, false);
            return;
        }

        try
        {
            // First attempt: use the standard MSDN approach.
            // This will throw an exception a directory is open in explorer
            Directory.Delete(dir, true);
        }
        catch (IOException)
        {
            // Try again to delete the directory manually recursing. 
            DeleteDirectory(dir, true);
        }
        catch (UnauthorizedAccessException)
        {
            // Try again to delete the directory manually recursing. 
            DeleteDirectory(dir, true);
        } 
    }
cdiggins
źródło
Jak więc usunąć folder, jeśli był UnauthorizedAccessException? Po prostu znowu rzuciłby. I ponownie. I znowu ... Ponieważ za każdym razem będzie przechodzić do catchi wywoływać funkcję ponownie. A Thread.Sleep(0);nie zmienia twoich uprawnień. W tym momencie powinien po prostu zapisać błąd i zakończyć się niepowodzeniem. Pętla ta będzie kontynuowana tak długo, jak długo (pod-) katalog będzie otwarty - nie zamyka go programowo. Czy jesteśmy gotowi na to pozwolić, dopóki te rzeczy pozostaną otwarte? Czy jest lepszy sposób?
vapcguy
Jeśli istnieje UnauthorizedAccessException, będzie próbował ręcznie ręcznie usunąć każdy plik. Więc nadal robi postępy, przechodząc do struktury katalogów. Tak, potencjalnie każdy plik i katalog zgłosi ten sam wyjątek, ale może to również wystąpić po prostu dlatego, że program explorer trzyma do niego uchwyt (patrz stackoverflow.com/a/1703799/184528 ). Zmienię „tryAgain” na „secondTry” aby było bardziej jasne.
cdiggins
Aby odpowiedzieć bardziej poprawnie, przekazuje „prawda” i wykonuje inną ścieżkę kodu.
cdiggins
Racja, widziałem twoją edycję, ale nie chodzi mi o usunięcie plików, ale o usunięcie katalogu. Napisałem kod, w którym mógłbym zrobić zasadniczo Process.Kill()na dowolnym procesie, przez który plik może zostać zablokowany i usunąć pliki. Problem, na który napotykam, polega na usuwaniu katalogu, w którym jeden z tych plików był nadal otwarty (patrz stackoverflow.com/questions/41841590/... ). Wracając przez tę pętlę, bez względu na to, co jeszcze zrobi, jeśli zrobi Directory.Delete()to ponownie w tym folderze, nadal zawiedzie, jeśli tego uchwytu nie można zwolnić.
vapcguy
To samo by się UnauthorizedAccessExceptionstało, odkąd usuwanie plików (zakładając, że było to nawet dozwolone, ponieważ aby dostać się do tego kodu, nie powiodło się Directory.Delete()) nie magicznie daje ci pozwolenia na usunięcie katalogu.
vapcguy
3

Żadne z powyższych rozwiązań nie działało dla mnie dobrze. Skończyło się na użyciu edytowanej wersji rozwiązania @ryascl, jak poniżej:

    /// <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))
        {
            Thread.Sleep(1);
            DeleteDir(directory);
        }
        DeleteDir(path);
    }

    private static void DeleteDir(string dir)
    {
        try
        {
            Thread.Sleep(1);
            Directory.Delete(dir, true);
        }
        catch (IOException)
        {
            DeleteDir(dir);
        }
        catch (UnauthorizedAccessException)
        {
            DeleteDir(dir);
        }
    }
Cahit Beyaz
źródło
2

Czy to możliwe, że masz wyścig, w którym inny wątek lub proces dodaje pliki do katalogu:

Sekwencja będzie wyglądać następująco:

Proces Deleter A:

  1. Opróżnij katalog
  2. Usuń (teraz pusty) katalog.

Jeśli ktoś inny doda plik między 1 a 2, to może 2 rzuciłby wymieniony wyjątek?

Douglas Leeder
źródło
2

Spędziłem kilka godzin, aby rozwiązać ten problem i inne wyjątki związane z usunięciem katalogu. To jest moje rozwiązanie

 public static void DeleteDirectory(string target_dir)
    {
        DeleteDirectoryFiles(target_dir);
        while (Directory.Exists(target_dir))
        {
            lock (_lock)
            {
                DeleteDirectoryDirs(target_dir);
            }
        }
    }

    private static void DeleteDirectoryDirs(string target_dir)
    {
        System.Threading.Thread.Sleep(100);

        if (Directory.Exists(target_dir))
        {

            string[] dirs = Directory.GetDirectories(target_dir);

            if (dirs.Length == 0)
                Directory.Delete(target_dir, false);
            else
                foreach (string dir in dirs)
                    DeleteDirectoryDirs(dir);
        }
    }

    private static void DeleteDirectoryFiles(string target_dir)
    {
        string[] files = Directory.GetFiles(target_dir);
        string[] dirs = Directory.GetDirectories(target_dir);

        foreach (string file in files)
        {
            File.SetAttributes(file, FileAttributes.Normal);
            File.Delete(file);
        }

        foreach (string dir in dirs)
        {
            DeleteDirectoryFiles(dir);
        }
    }

Ten kod ma małe opóźnienie, co nie jest ważne dla mojej aplikacji. Ale bądź ostrożny, opóźnienie może być dla ciebie problemem, jeśli masz wiele podkatalogów w katalogu, który chcesz usunąć.

Demid
źródło
8
-1 Na ​​czym polega opóźnienie? Proszę nie programować przypadkowo!
Ruben Bartelink,
1
@Ruben Nie powiedziałem, że się mylisz. Powiedziałem tylko, że głosowanie w tej sprawie jest surową karą. Zgadzam się z tobą, jednak 4 głosy poparcia nie spowodowały 4 głosów negatywnych. Chciałbym również głosować za waszym komentarzem, ale nie głosowałbym za odpowiedzią z powodu niewyjaśnionego opóźnienia :)
ThunderGr
1
@ RubenBartelink i inni: chociaż nie podoba mi się ten kod (opublikowałem inne rozwiązanie o podobnym podejściu), opóźnienie tutaj jest rozsądne. Problem najprawdopodobniej pozostaje poza kontrolą aplikacji; być może inna aplikacja okresowo skanuje FS, blokując w ten sposób folder na krótki czas. Opóźnienie rozwiązuje problem, odliczanie raportu o błędzie do zera. Kogo to obchodzi, jeśli nie mamy cholernego pomysłu na pierwotną przyczynę?
Andrey Tarantsov
1
@ RubenBartelink W rzeczywistości, gdy się nad tym zastanowić, nie stosowanie podejścia opóźniania i ponawiania podczas usuwania katalogu NTFS jest tutaj nieodpowiedzialnym rozwiązaniem. Każdy rodzaj trwającego przechodzenia pliku blokuje usuwanie, więc prędzej czy później zakończy się niepowodzeniem. Nie można oczekiwać, że wszystkie narzędzia do wyszukiwania, tworzenia kopii zapasowych, antywirusowe i zarządzania plikami innych firm pozostaną poza folderem.
Andrey Tarantsov
1
@ RubenBartelink Kolejny przykład, powiedzmy, że dajesz opóźnienie 100 ms, a najwyższy czas blokady dowolnego oprogramowania na docelowym komputerze to oprogramowanie AV = 90 ms. Powiedzmy, że ma także oprogramowanie do tworzenia kopii zapasowych, które blokuje pliki na 70 ms. Teraz AV blokuje plik, aplikacja czeka 100 ms, co normalnie jest w porządku, ale następnie napotyka kolejną blokadę, ponieważ oprogramowanie do tworzenia kopii zapasowych zaczyna pobierać plik po znaku 70 ms skanowania AV, więc wydanie pliku zajmie kolejne 40 ms. Więc chociaż oprogramowanie AV trwa dłużej, a Twoje 100 ms jest zwykle dłuższe niż jedna z dwóch aplikacji, nadal musisz wziąć pod uwagę, kiedy zaczyna się w środku.
vapcguy
2

Rekurencyjne usuwanie katalogu, który nie usuwa plików, jest z pewnością nieoczekiwane. Moja poprawka do tego:

public class IOUtils
{
    public static void DeleteDirectory(string directory)
    {
        Directory.GetFiles(directory, "*", SearchOption.AllDirectories).ForEach(File.Delete);
        Directory.Delete(directory, true);
    }
}

Wystąpiły przypadki, w których to pomogło, ale ogólnie Directory.Delete usuwa pliki wewnątrz katalogów po rekurencyjnym usuwaniu Wystąpiły udokumentowano w msdn .

Od czasu do czasu spotykam się z tym nieregularnym zachowaniem również jako użytkownik Eksploratora Windows: Czasami nie mogę usunąć folderu (wydaje się, że nonsensowny komunikat to „odmowa dostępu”), ale kiedy przechodzę do szczegółów i usuwam dolne elementy, mogę następnie usunąć górną przedmioty również. Sądzę więc, że powyższy kod dotyczy anomalii systemu operacyjnego - nie problemu biblioteki podstawowej.

citykid
źródło
1

Katalog lub plik w nim jest zablokowany i nie można go usunąć. Znajdź winowajcę, który go blokuje, i sprawdź, czy możesz go wyeliminować.

Vilx-
źródło
T1000 do użytkownika z otwartym folderem: „Jesteś zakończony!”
vapcguy
1

Wygląda na to, że wybranie ścieżki lub podfolderu w Eksploratorze Windows wystarczy, aby zablokować pojedyncze wykonanie Directory.Delete (ścieżka, prawda), zgłaszając wyjątek IOException, jak opisano powyżej, i umiera zamiast uruchamiać Eksploratora Windows do folderu nadrzędnego i postępować jak spodziewany.

David Alpert
źródło
To wydaje się być moim problemem. Jak tylko zamknąłem Eksploratora i uruchomiłem ponownie, bez wyjątku. Nawet wybranie rodzica rodzica nie wystarczyło. Musiałem faktycznie zamknąć Eksploratora.
Scott Marlowe
Tak, to się dzieje i jest przyczyną. Czy masz jakiś pomysł, jak programowo sobie z tym poradzić, czy też odpowiedzią jest zawsze upewnienie się, że wszyscy 1000 użytkowników ma ten folder zamknięty?
vapcguy
1

Miałem dzisiaj ten problem. Działo się tak, ponieważ miałem eksploratora Windows otwartego na katalog, który próbował zostać usunięty, powodując niepowodzenie wywołania rekurencyjnego, a tym samym wyjątek IOException. Upewnij się, że nie ma otwartych uchwytów do katalogu.

Ponadto MSDN jest jasne, że nie musisz pisać własnych wniosków: http://msdn.microsoft.com/en-us/library/fxeahc5f.aspx

GrokSrc
źródło
1

Miałem ten sam problem z Windows Workflow Foundation na serwerze kompilacji z TFS2012. Wewnętrznie przepływ pracy o nazwie Directory.Delete () z flagą rekurencyjną ustawioną na true. W naszym przypadku wydaje się, że jest związany z siecią.

Usuwaliśmy binarny folder upuszczania z udziału sieciowego przed ponownym utworzeniem i ponownym zapełnieniem go najnowszymi plikami binarnymi. Każda inna kompilacja zawiedzie. Podczas otwierania folderu upuszczania po nieudanej kompilacji folder był pusty, co wskazuje, że każdy aspekt wywołania Directory.Delete () był udany, z wyjątkiem usunięcia faktycznie katalogu.

Problem wydaje się być spowodowany asynchroniczną naturą komunikacji plików sieciowych. Serwer kompilacji powiedział serwerowi plików, aby usunął wszystkie pliki, a serwer plików zgłosił, że tak, mimo że nie został całkowicie ukończony. Następnie serwer kompilacji zażądał usunięcia katalogu, a serwer plików odrzucił żądanie, ponieważ nie zakończył całkowicie usuwania plików.

Dwa możliwe rozwiązania w naszym przypadku:

  • Zbuduj rekurencyjne usuwanie w naszym własnym kodzie z opóźnieniami i weryfikacjami między każdym krokiem
  • Spróbuj ponownie do X razy po IOException, dając opóźnienie przed ponowną próbą

Ta ostatnia metoda jest szybka i brudna, ale wydaje się, że załatwia sprawę.

Shaun
źródło
1

Wynika to z FileChangesNotifications.

Dzieje się tak od czasu ASP.NET 2.0. Usunięty folder w aplikacji zostanie ponownie uruchomiony . Możesz to zobaczyć sam, korzystając z monitorowania kondycji ASP.NET .

Po prostu dodaj ten kod do swojego web.config / configuration / system.web:

<healthMonitoring enabled="true">
  <rules>
    <add name="MyAppLogEvents" eventName="Application Lifetime Events" provider="EventLogProvider" profile="Critical"/>
  </rules>
</healthMonitoring>


Następnie sprawdź Windows Log -> Application. Co się dzieje:

Po usunięciu folderu, jeśli istnieje jakiś podfolder, Delete(path, true)najpierw usuwa podfolder. Wystarczy, aby FileChangesMonitor wiedział o usuwaniu i wyłączaniu aplikacji. Tymczasem twój katalog główny nie został jeszcze usunięty. To jest wydarzenie z Log:


wprowadź opis zdjęcia tutaj


Delete() nie zakończył pracy, a ponieważ aplikacja się wyłącza, pojawia się wyjątek:

wprowadź opis zdjęcia tutaj

Jeśli nie masz żadnych podfolderów w usuwanym folderze, Delete () po prostu usuwa wszystkie pliki i ten folder, aplikacja również jest restartowana, ale nie otrzymujesz żadnych wyjątków , ponieważ ponowne uruchomienie aplikacji niczego nie przerywa. Ale nadal tracisz wszystkie sesje w toku, aplikacja nie odpowiada na żądania przy ponownym uruchomieniu itp.

Co teraz?

Istnieją pewne obejścia i poprawki, aby wyłączyć to zachowanie, łączenie katalogów , wyłączanie FCN za pomocą rejestru , zatrzymywanie FileChangesMonitor za pomocą Reflection (ponieważ nie ma metody narażonej) , ale nie wszystkie wydają się mieć rację, ponieważ FCN jest dostępny dla powód. Dba o strukturę Twojej aplikacji , która nie jest strukturą Twoich danych . Krótka odpowiedź brzmi: umieść foldery, które chcesz usunąć, poza aplikacją. FileChangesMonitor nie otrzyma żadnych powiadomień, a aplikacja nie będzie ponownie uruchamiana za każdym razem. Nie dostaniesz żadnych wyjątków. Aby były widoczne z Internetu, istnieją dwa sposoby:

  1. Utwórz kontroler, który obsługuje połączenia przychodzące, a następnie odsyła pliki, odczytując z folderu poza aplikacją (poza wwwroot).

  2. Jeśli Twój projekt jest duży, a wydajność jest najważniejsza, skonfiguruj osobny mały i szybki serwer WWW do obsługi zawartości statycznej. W ten sposób IIS pozostawi swoją konkretną pracę. Może to być ten sam komputer (mongoose dla Windows) lub inny komputer (nginx dla Linux). Dobra wiadomość jest taka, że ​​nie musisz płacić dodatkowej licencji Microsoft, aby skonfigurować statyczny serwer treści na Linuksie.

Mam nadzieję że to pomoże.

rzymski
źródło
1

Jak wspomniano powyżej, „zaakceptowane” rozwiązanie zawodzi przy ponownej analizie punktów - ale ludzie nadal go oceniają (???). Istnieje o wiele krótsze rozwiązanie, które poprawnie powiela funkcjonalność:

public static void rmdir(string target, bool recursive)
{
    string tfilename = Path.GetDirectoryName(target) +
        (target.Contains(Path.DirectorySeparatorChar.ToString()) ? Path.DirectorySeparatorChar.ToString() : string.Empty) +
        Path.GetRandomFileName();
    Directory.Move(target, tfilename);
    Directory.Delete(tfilename, recursive);
}

Wiem, że nie obsługuje przypadków uprawnień wymienionych później, ale dla wszystkich zamiarów i celów FAR LEPIEJ zapewnia oczekiwaną funkcjonalność oryginalnego katalogu / stock.Delete () - oraz z dużo mniejszym kodem .

Możesz bezpiecznie kontynuować przetwarzanie, ponieważ stary katalog nie będzie przeszkadzał ... nawet jeśli nie zniknie, ponieważ „system plików wciąż nadrabia zaległości” (lub jakąkolwiek wymówkę, jaką MS podało, by zapewnić zepsutą funkcję) .

Jako korzyść, jeśli wiesz, że twój katalog docelowy jest duży / głęboki i nie chcesz czekać (lub zawracać sobie głowy wyjątkami), ostatni wiersz można zastąpić:

    ThreadPool.QueueUserWorkItem((o) => { Directory.Delete(tfilename, recursive); });

Nadal możesz bezpiecznie kontynuować pracę.

Obrabować
źródło
2
Czy twoje zadanie można uprościć: ciąg tfilename = Path.Combine (Path.GetDirectoryName (target), Path.GetRandomFileName ());
Pete
1
Muszę się zgodzić z Petem. Kod jak napisano nie doda separatora. Wziął moją ścieżkę \\server\C$\diri udało się \\server\C$asf.yuw. W rezultacie otrzymałem błąd na Directory.Move()- Source and destination path must have identical roots. Move will not work across volumes. Działało dobrze, kiedy użyłem kodu Pete'a Z WYJĄTKIEM ani nie obsługuje, gdy są zablokowane pliki lub otwarte katalogi - więc nigdy nie dostaje się do ThreadPoolpolecenia.
vapcguy
PRZESTROGA: tej odpowiedzi należy używać tylko z parametrem recursive = true. W przypadku wartości false spowoduje to przeniesienie katalogu, nawet jeśli nie jest on pusty. Co byłoby błędem; poprawne zachowanie w takim przypadku polega na zgłoszeniu wyjątku i pozostawieniu katalogu bez zmian.
ToolmakerSteve
1

Ten problem może pojawić się w systemie Windows, gdy w katalogu (lub w dowolnym podkatalogu) znajdują się pliki, których długość ścieżki jest większa niż 260 symboli.

W takich przypadkach musisz usunąć \\\\?\C:\mydirzamiast C:\mydir. O limicie 260 symboli możesz przeczytać tutaj .

HostageBrain
źródło
1

Rozwiązałem tę tysiącletnią technikę (możesz opuścić Nić, sam spać w haczyku)

bool deleted = false;
        do
        {
            try
            {
                Directory.Delete(rutaFinal, true);                    
                deleted = true;
            }
            catch (Exception e)
            {
                string mensaje = e.Message;
                if( mensaje == "The directory is not empty.")
                Thread.Sleep(50);
            }
        } while (deleted == false);
Mauricio Rdz
źródło
0

Jeśli bieżący katalog aplikacji (lub innej aplikacji) to ten, który próbujesz usunąć, nie będzie to błąd naruszenia zasad dostępu, ale katalog nie będzie pusty. Upewnij się, że nie jest to twoja aplikacja, zmieniając bieżący katalog; Upewnij się również, że katalog nie jest otwarty w jakimś innym programie (np. Word, Excel, Total Commander itp.). Większość programów przejdzie do katalogu ostatnio otwartego pliku, co spowodowałoby to.

konfigurator
źródło
0

w przypadku plików sieciowych Directory.DeleteHelper (rekursywny: = prawda) może powodować wyjątek IOException, który jest spowodowany opóźnieniem usunięcia pliku

zatłoczony
źródło
0

Myślę, że istnieje jakiś plik otwarty przez jakiś strumień, o którym nie wiesz, że miałem ten sam problem i rozwiązałem go, zamykając wszystkie strumienie wskazujące katalog, który chciałem usunąć.

Ahmad Moussa
źródło
0

Ten błąd występuje, jeśli jakikolwiek plik lub katalog zostanie uznany za używany. Jest to błąd wprowadzający w błąd. Sprawdź, czy masz jakieś okna eksploratora lub okna wiersza poleceń otwarte dla dowolnego katalogu w drzewie lub programu, który używa pliku w tym drzewie.

allen1
źródło
0

Rozwiązałem jeden możliwy przypadek podanego problemu, gdy metody były asynchronizowane i kodowane w następujący sposób:

// delete any existing update content folder for this update
if (await fileHelper.DirectoryExistsAsync(currentUpdateFolderPath))
       await fileHelper.DeleteDirectoryAsync(currentUpdateFolderPath);

Z tym:

bool exists = false;                
if (await fileHelper.DirectoryExistsAsync(currentUpdateFolderPath))
    exists = true;

// delete any existing update content folder for this update
if (exists)
    await fileHelper.DeleteDirectoryAsync(currentUpdateFolderPath);

Wniosek? Istnieje pewien asynchroniczny aspekt pozbycia się uchwytu używanego do sprawdzania istnienia, z którym Microsoft nie był w stanie rozmawiać. To tak, jakby metoda asynchroniczna w instrukcji if miała instrukcję if działającą jak instrukcja using.

Pat Pattillo
źródło
0

Nie musisz tworzyć dodatkowych metod rekurencyjności ani usuwać plików w folderze dodatkowym. Wszystko to odbywa się automatycznie przez telefon

DirectoryInfo.Delete ();

Szczegóły są tutaj .

Coś takiego działa całkiem dobrze:

  var directoryInfo = new DirectoryInfo("My directory path");
    // Delete all files from app data directory.

    foreach (var subDirectory in directoryInfo.GetDirectories())
    {
          subDirectory.Delete(true);// true set recursive paramter, when it is true delete sub file and sub folder with files too
    }

przekazanie wartości true jako zmiennej w celu usunięcia metody spowoduje usunięcie również plików podrzędnych i podfolderów z plikami.

nzrytmn
źródło
-2

Żadna z powyższych odpowiedzi nie działała dla mnie. Wygląda na to, że użycie mojej aplikacji DirectoryInfodo katalogu docelowego spowodowało, że pozostała zablokowana.

Wymuszanie wyrzucania elementów bezużytecznych rozwiązało problem, ale nie od razu. Kilka prób usunięcia w razie potrzeby.

Zwróć uwagę na Directory.Existsto, że może zniknąć po wyjątku. Nie wiem, dlaczego usunięcie było dla mnie opóźnione (Windows 7 SP1)

        for (int attempts = 0; attempts < 10; attempts++)
        {
            try
            {
                if (Directory.Exists(folder))
                {
                    Directory.Delete(folder, true);
                }
                return;
            }
            catch (IOException e)
            {
                GC.Collect();
                Thread.Sleep(1000);
            }
        }

        throw new Exception("Failed to remove folder.");
Reactgular
źródło
1
-1 Programowanie przez przypadek. Co robi co, gdy GC'd? Czy to w jakikolwiek sposób dobra ogólna rada? (Wierzę, że kiedy mówisz, że masz problem i że użyłeś tego kodu i że czujesz, że nie masz teraz problemu, ale to nie o to chodzi)
Ruben Bartelink
@RubenBartelink Zgadzam się. To jest hack. Kod Voodoo, który robi coś, gdy nie jest jasne, co rozwiązuje i jak. Chciałbym właściwe rozwiązanie.
Reactgular,
1
Mój problem polega na tym, że wszystko, co dodaje ponad stackoverflow.com/a/14933880/11635, jest wysoce spekulacyjne. Gdybym mógł, dałbym -1 za duplikację i -1 za spekulację / programowanie przez przypadek. Zraszanie GC.Collectto a) po prostu zła rada ib) niewystarczająco powszechna ogólna przyczyna zablokowanych reżimów, aby zasługiwać na włączenie tutaj. Po prostu wybierz jedną z pozostałych i nie siej więcej zamieszania w umysłach niewinnych czytelników
Ruben Bartelink
3
Użyj GC.WaitForPendingFinalizers (); po GC.Collect (); to zadziała zgodnie z oczekiwaniami.
Heiner
Nie jestem pewien, nie przetestowałem, ale być może lepiej byłoby zrobić coś z usingoświadczeniem, a następnie: using (DirectoryInfo di = new DirectoryInfo(@"c:\MyDir")) { for (int attempts = 0; attempts < 10; attempts++) { try { if (di.Exists(folder)) { Directory.Delete(folder, true); } return; } catch (IOException e) { Thread.Sleep(1000); } } }
vapcguy
-3

dodaj true w drugim param.

Directory.Delete(path, true);

Usunie wszystko.

Santiago Medina Chaverra
źródło
1
Directory.Delete (ścieżka, prawda); było oryginalne pytanie
Pisu