Verzeichnis mit Directory.Delete kann nicht gelöscht werden (Pfad, true)

383

Ich verwende .NET 3.5 und versuche, ein Verzeichnis rekursiv zu löschen, indem ich:

Directory.Delete(myPath, true);

Meines Wissens nach sollte dies ausgelöst werden, wenn Dateien verwendet werden oder ein Berechtigungsproblem vorliegt. Andernfalls sollte das Verzeichnis und der gesamte Inhalt gelöscht werden.

Ich bekomme jedoch gelegentlich Folgendes:

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

Ich bin nicht überrascht, dass die Methode manchmal auslöst, aber ich bin überrascht, diese bestimmte Nachricht zu erhalten, wenn rekursiv wahr ist. (Ich weiß, dass das Verzeichnis nicht leer ist.)

Gibt es einen Grund, warum ich dies anstelle von AccessViolationException sehen würde?

Jason Anderson
quelle
13
AccessViolationException wird nicht angezeigt - dies gilt für ungültige Zeigeroperationen, nicht für den Datenträgerzugriff.
Joe White
1
Dies scheint eine Art E / A-Problem zu sein, außer dass das Verzeichnis nicht leer ist, z. B. offene Dateihandles oder ähnliches. Ich würde versuchen, die rekursive Löschoption zu verwenden, dann in einem Catch für IOException nach geöffneten Dateihandles suchen und diese schließen und dann erneut versuchen. Es gibt eine Diskussion darüber hier: stackoverflow.com/questions/177146/…
Dan Csharpster

Antworten:

231

Anmerkung des Herausgebers: Obwohl diese Antwort einige nützliche Informationen enthält, ist sie in Bezug auf die Funktionsweise von sachlich falsch Directory.Delete. Bitte lesen Sie die Kommentare zu dieser Antwort und andere Antworten auf diese Frage.


Ich bin schon einmal auf dieses Problem gestoßen.

Die Ursache des Problems liegt darin, dass diese Funktion keine Dateien löscht, die sich in der Verzeichnisstruktur befinden. Sie müssen also eine Funktion erstellen, die alle Dateien in der Verzeichnisstruktur und dann alle Verzeichnisse löscht, bevor Sie das Verzeichnis selbst entfernen. Ich weiß, dass dies gegen den zweiten Parameter verstößt, aber es ist ein viel sichererer Ansatz. Darüber hinaus möchten Sie wahrscheinlich READ-ONLY-Zugriffsattribute aus den Dateien entfernen, bevor Sie sie löschen. Andernfalls wird eine Ausnahme ausgelöst.

Fügen Sie diesen Code einfach in Ihr Projekt ein.

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

Außerdem füge ich persönlich eine Einschränkung für Bereiche des Computers hinzu, die gelöscht werden dürfen, weil Sie möchten, dass jemand diese Funktion auf C:\WINDOWS (%WinDir%)oder aufruft C:\.

Jeremy Edwards
quelle
117
Das ist Unsinn. Directory.Delete (myPath, true) ist eine Überladung, die alle Dateien löscht, die sich in der Verzeichnisstruktur befinden. Wenn Sie sich irren wollen, irren Sie sich mit der Antwort von Ryan S.
Sig. Tolleranza
35
+1, da Directory.Delete () zwar Dateien in seinen Unterverzeichnissen löscht (mit recursive = true), jedoch eine "IOException: Directory is not empty" auslöst, wenn eines der Unterverzeichnisse oder Dateien schreibgeschützt ist. Diese Lösung funktioniert also besser als Directory.Delete ()
Anthony Brien
17
Ihre Aussage, Directory.Delete(path, true)die keine Dateien löscht, ist falsch. Siehe MSDN msdn.microsoft.com/en-us/library/fxeahc5f.aspx
Konstantin Spirin
20
-1 Kann jemand bitte deutlich machen, dass die Gültigkeit dieses Ansatzes sehr zweifelhaft ist? Wenn dies Directory.Delete(string,bool)fehlschlägt, ist etwas gesperrt oder falsch zugelassen, und es gibt keine einheitliche Lösung für ein solches Problem. Die Leute müssen dieses Problem in ihrem Kontext ansprechen, und wir sollten nicht jede Idee mit dem Problem (mit Wiederholungsversuchen und Ausnahmeverschlucken) in den Griff bekommen und auf ein gutes Ergebnis hoffen.
Ruben Bartelink
37
Hüten Sie sich vor diesem Ansatz, wenn Ihr Verzeichnis, das Sie löschen, Verknüpfungen / symbolische Links zu anderen Ordnern enthält - möglicherweise löschen Sie am Ende mehr als erwartet
Chanakya
182

Wenn Sie versuchen, das Verzeichnis rekursiv zu löschen, aund das Verzeichnis a\bim Explorer geöffnet ist, bwird es gelöscht, aber Sie erhalten die Fehlermeldung "Verzeichnis ist nicht leer" a, obwohl es leer ist, wenn Sie nachsehen. Das aktuelle Verzeichnis einer Anwendung (einschließlich Explorer) behält ein Handle für das Verzeichnis bei . Wenn Sie anrufen Directory.Delete(true), löscht sie von unten nach oben: b, dann a. Wenn bes im Explorer geöffnet ist, erkennt der Explorer das Löschen von b, wechselt das Verzeichnis nach oben cd ..und bereinigt offene Handles. Da das Dateisystem asynchron arbeitet, Directory.Deleteschlägt der Vorgang aufgrund von Konflikten mit dem Explorer fehl.

Unvollständige Lösung

Ich habe ursprünglich die folgende Lösung veröffentlicht, mit der Idee, den aktuellen Thread zu unterbrechen, damit der Explorer Zeit hat, das Verzeichnishandle freizugeben.

// incomplete!
try
{
    Directory.Delete(path, true);
}
catch (IOException)
{
    Thread.Sleep(0);
    Directory.Delete(path, true);
}

Dies funktioniert jedoch nur, wenn das geöffnete Verzeichnis das unmittelbare untergeordnete Verzeichnis des zu löschenden Verzeichnisses ist . Wenn a\b\c\dim Explorer geöffnet ist und Sie diese aOption verwenden, schlägt diese Technik nach dem Löschen von dund fehl c.

Eine etwas bessere Lösung

Diese Methode behandelt das Löschen einer tiefen Verzeichnisstruktur, selbst wenn eines der untergeordneten Verzeichnisse im Explorer geöffnet ist.

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

Trotz der zusätzlichen Arbeit, alleine zu rekursieren, müssen wir uns immer noch darum kümmernUnauthorizedAccessException , was auf dem Weg passieren kann. Es ist nicht klar, ob der erste Löschversuch den Weg für den zweiten, erfolgreichen Versuch ebnet oder ob es nur die durch das Auslösen / Abfangen einer Ausnahme verursachte Zeitverzögerung ist, die es dem Dateisystem ermöglicht, aufzuholen.

Möglicherweise können Sie die Anzahl der Ausnahmen reduzieren, die unter typischen Bedingungen ausgelöst und abgefangen werden, indem Sie Thread.Sleep(0)am Anfang des tryBlocks ein hinzufügen . Darüber hinaus besteht das Risiko, dass Sie unter starker Systemlast beide Directory.DeleteVersuche durchfliegen und fehlschlagen. Betrachten Sie diese Lösung als Ausgangspunkt für ein robusteres rekursives Löschen.

Allgemeine Antwort

Diese Lösung behandelt nur die Besonderheiten der Interaktion mit Windows Explorer. Wenn Sie einen soliden Löschvorgang wünschen, sollten Sie beachten, dass alles (Virenscanner, was auch immer) jederzeit einen offenen Griff zu dem haben kann, was Sie löschen möchten. Sie müssen es also später erneut versuchen. Wie viel später und wie oft Sie es versuchen, hängt davon ab, wie wichtig es ist, dass das Objekt gelöscht wird. Wie MSDN angibt ,

Robuster Dateiterationscode muss viele Komplexitäten des Dateisystems berücksichtigen.

Diese unschuldige Aussage, die nur einen Link zur NTFS-Referenzdokumentation enthält, sollte Ihre Haare aufstehen lassen.

( Bearbeiten : Viel. Diese Antwort hatte ursprünglich nur die erste, unvollständige Lösung.)

ryscl
quelle
11
Es scheint, als würde Directory.Delete (path, true) aufgerufen, während path oder einer der Ordner / Dateien unter path geöffnet oder im Windows Explorer ausgewählt ist und eine IOException auslöst. Das Schließen des Windows Explorers und das erneute Ausführen meines vorhandenen Codes ohne den oben vorgeschlagenen Versuch / Fang funktionierten einwandfrei.
David Alpert
1
Ich kann nicht verstehen, wie und warum es funktioniert, aber es hat bei mir funktioniert, während ich Dateiattribute festgelegt und meine eigene rekursive Funktion geschrieben habe.
Stilgar
1
@CarlosLiu Weil es "Explorer eine Chance gibt, das Verzeichnis-Handle freizugeben"
Dmitry Gonchar
4
Was passiert ist, dass das System den Explorer auffordert, "das Verzeichnis-Handle freizugeben" und dann versucht, das Verzeichnis zu löschen. Wenn das Verzeichnishandle nicht rechtzeitig gelöscht wurde, wird eine Ausnahme ausgelöst und der catchBlock ausgeführt (währenddessen gibt der Explorer das Verzeichnis immer noch frei, da kein Befehl gesendet wurde, um es anzuweisen, dies nicht zu tun). Der Aufruf von Thread.Sleep(0)kann oder kann nicht notwendig sein, da der catchBlock dem System bereits etwas mehr Zeit gegeben hat, aber ein wenig zusätzliche Sicherheit zu geringen Kosten bietet. Danach wird das Deleteaufgerufen, wobei das Verzeichnis bereits freigegeben ist.
Zachary Kniebel
1
@PandaWood eigentlich hat nur dieser Schlaf (100) für mich funktioniert. Schlaf (0) hat nicht funktioniert. Ich habe keine Ahnung, was los ist und wie ich das richtig lösen kann. Ich meine, was ist, wenn es von der Serverlast abhängt und in Zukunft 300 oder 400 sein sollten? Woher das wissen? Muss ein anderer richtiger Weg sein ...
Roman
43

Bevor Sie fortfahren, überprüfen Sie die folgenden Gründe, auf die Sie Einfluss haben:

  • Ist der Ordner als aktuelles Verzeichnis Ihres Prozesses festgelegt? Wenn ja, ändern Sie es zuerst in etwas anderes.
  • Haben Sie eine Datei aus diesem Ordner geöffnet (oder eine DLL geladen)? (und vergessen, es zu schließen / entladen)

Überprüfen Sie andernfalls die folgenden legitimen Gründe, die außerhalb Ihrer Kontrolle liegen:

  • In diesem Ordner befinden sich Dateien, die als schreibgeschützt markiert sind.
  • Sie haben keine Löschberechtigung für einige dieser Dateien.
  • Die Datei oder der Unterordner ist im Explorer oder in einer anderen App geöffnet.

Wenn eines der oben genannten Probleme auftritt, sollten Sie verstehen, warum dies geschieht, bevor Sie versuchen, Ihren Löschcode zu verbessern. Sollte Ihre App schreibgeschützte oder unzugängliche Dateien löschen? Wer hat sie so markiert und warum?

Wenn Sie die oben genannten Gründe ausgeschlossen haben, besteht immer noch die Möglichkeit von falschen Fehlern. Das Löschen schlägt fehl, wenn jemand ein Handle für eine der zu löschenden Dateien oder Ordner hat, und es gibt viele Gründe, warum jemand den Ordner auflistet oder seine Dateien liest:

  • Indexer suchen
  • Antiviren
  • Backup-Software

Der allgemeine Ansatz zur Behandlung von Störfehlern besteht darin, es mehrmals zu versuchen und zwischen den Versuchen eine Pause einzulegen. Sie möchten es offensichtlich nicht für immer versuchen, also sollten Sie nach einer bestimmten Anzahl von Versuchen aufgeben und entweder eine Ausnahme auslösen oder den Fehler ignorieren. So was:

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
}

Meiner Meinung nach sollte ein solcher Helfer für alle Löschungen verwendet werden, da immer falsche Fehler möglich sind. Sie sollten diesen Code jedoch an Ihren Verwendungsfall anpassen und nicht nur blind kopieren.

Ich hatte falsche Fehler für einen internen Datenordner, der von meiner App unter% LocalAppData% generiert wurde. Meine Analyse sieht also folgendermaßen aus:

  1. Der Ordner wird ausschließlich von meiner Anwendung gesteuert, und der Benutzer hat keinen gültigen Grund, Dinge in diesem Ordner als schreibgeschützt oder unzugänglich zu markieren. Daher versuche ich nicht, diesen Fall zu behandeln.

  2. Es gibt dort keine wertvollen, vom Benutzer erstellten Inhalte, sodass kein Risiko besteht, versehentlich etwas mit Gewalt zu löschen.

  3. Als interner Datenordner erwarte ich nicht, dass er im Explorer geöffnet ist, zumindest habe ich nicht das Bedürfnis, den Fall speziell zu behandeln (dh ich kann diesen Fall über den Support gut bearbeiten).

  4. Wenn alle Versuche fehlschlagen, ignoriere ich den Fehler. Im schlimmsten Fall kann die App einige neuere Ressourcen nicht entpacken, stürzt ab und fordert den Benutzer auf, sich an den Support zu wenden. Dies ist für mich akzeptabel, solange dies nicht häufig vorkommt. Wenn die App nicht abstürzt, bleiben einige alte Daten zurück, was für mich wiederum akzeptabel ist.

  5. Ich beschließe, Wiederholungsversuche auf 500 ms (50 * 10) zu beschränken. Dies ist eine willkürliche Schwelle, die in der Praxis funktioniert. Ich wollte, dass der Schwellenwert kurz genug ist, damit Benutzer die App nicht beenden, weil sie nicht mehr reagiert. Andererseits reicht eine halbe Sekunde für den Täter aus, um die Bearbeitung meines Ordners abzuschließen. Nach anderen SO-Antworten zu urteilen, die sich manchmal sogar Sleep(0)als akzeptabel herausstellen, werden nur sehr wenige Benutzer jemals mehr als einen einzigen Wiederholungsversuch erleben.

  6. Ich versuche es alle 50 ms erneut, was eine weitere willkürliche Zahl ist. Ich bin der Meinung, dass, wenn eine Datei beim Versuch, sie zu löschen, verarbeitet (indiziert, überprüft) wird, 50 ms ungefähr der richtige Zeitpunkt sind, um zu erwarten, dass die Verarbeitung in meinem Fall abgeschlossen ist. Außerdem sind 50 ms klein genug, um nicht zu einer merklichen Verlangsamung zu führen. Auch dies Sleep(0)scheint in vielen Fällen ausreichend zu sein, sodass wir nicht zu viel verzögern möchten.

  7. Der Code wiederholt alle E / A-Ausnahmen. Normalerweise erwarte ich keine Ausnahmen beim Zugriff auf% LocalAppData%. Daher habe ich mich für die Einfachheit entschieden und das Risiko einer Verzögerung von 500 ms für den Fall einer legitimen Ausnahme akzeptiert. Ich wollte auch keinen Weg finden, um die genaue Ausnahme zu erkennen, die ich erneut versuchen möchte.

Andrey Tarantsov
quelle
7
PPS Einige Monate später freue ich mich, Ihnen mitteilen zu können, dass dieser (etwas verrückte) Code das Problem vollständig gelöst hat. Supportanfragen zu diesem Problem sind auf Null gesunken (von ca. 1-2 pro Woche).
Andrey Tarantsov
1
+0 Während dies robuster und weniger ist, ist es hier; Die perfekte Lösung für Sie 'als stackoverflow.com/a/7518831/11635 , für mich gilt das Gleiche - zufällig programmieren - vorsichtig behandeln. Ein nützlicher Punkt in Ihrem Code ist, dass Sie, wenn Sie einen erneuten Versuch durchführen Directory.Existsmöchten , berücksichtigen müssen, dass Sie sich in einem Rennen mit der Unklarheit befinden, ob das Verzeichnis seit dem letzten Versuch "gegangen" ist [und ein Niave- Wächter dies tun würde nicht lösen.]
Ruben Bartelink
1
Ich liebe es ... weiß nicht, was ich tue, dass dies immer ein Schmerzpunkt für mich ist ... aber es liegt nicht daran, dass ich das Verzeichnis im Explorer geöffnet habe ... nicht viel Aufruhr im Internet darüber mehr -oder-weniger Fehler ... zumindest ich und Andrey haben eine Möglichkeit, damit umzugehen :)
TCC
2
@RubenBartelink OK, ich denke, wir können uns darauf einigen: Das Posten eines Codes, der für eine bestimmte App funktioniert (und nie für jeden Fall geeignet sein sollte), da eine SO-Antwort für viele Anfänger und Anfänger ein schlechter Dienst sein wird / oder unwissende Entwickler. Ich habe es als Ausgangspunkt für die Anpassung angegeben, aber ja, einige Leute werden es so verwenden, wie es ist, und das ist eine schlechte Sache.
Andrey Tarantsov
2
@nopara Du brauchst den Vergleich nicht; Wenn wir nicht auf dem Laufenden sind, sind wir gescheitert. Und ja, in vielen Fällen möchten Sie eine Ausnahme auslösen und dann den entsprechenden Fehlerbehandlungscode im Stapel hinzufügen, wahrscheinlich mit einer vom Benutzer sichtbaren Nachricht.
Andrey Tarantsov
18

Moderne asynchrone Antwort

Die akzeptierte Antwort ist einfach falsch. Sie funktioniert möglicherweise für einige Benutzer, da die Zeit, die zum Abrufen von Dateien von der Festplatte benötigt wird, alles freisetzt, was die Dateien gesperrt hat. Tatsache ist, dass dies passiert, weil Dateien durch einen anderen Prozess / Stream / eine andere Aktion gesperrt werden. Die anderen Antworten verwenden Thread.Sleep(Yuck), um das Löschen des Verzeichnisses nach einiger Zeit erneut zu versuchen. Diese Frage muss mit einer moderneren Antwort überarbeitet werden.

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

Unit Tests

Diese Tests zeigen ein Beispiel dafür, wie eine gesperrte Datei Directory.Deletezum Fehlschlagen führen kann und wie die TryDeleteDirectoryoben beschriebene Methode das Problem behebt.

[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
quelle
Können Sie erweitern, was Sie unter "modern" verstehen? Was sind die Vorteile Ihres Ansatzes? Warum sind die anderen Ihrer Meinung nach falsch?
TinyRacoon
1
Andere sind nicht falsch. Sie verwenden nur ältere APIs, wie Thread.SleepSie sie heute vermeiden sollten, und verwenden stattdessen async/ awaitmit Task.Delay. Das ist verständlich, das ist eine sehr alte Frage.
Muhammad Rehan Saeed
Dieser Ansatz funktioniert in VB.Net nicht (zumindest nicht mit einer sehr wörtlichen Zeile-für-Zeile-Konvertierung), daBC36943 'Await' cannot be used inside a 'Catch' statement, a 'Finally' statement, or a 'SyncLock' statement.
amonroejj
@amonroejj Sie müssen eine ältere Version verwenden. Das wurde behoben.
Muhammad Rehan Saeed
Kleine Verbesserung statt return true if (!Directory.Exists(directoryPath)) { return true; } await Task.Delay(millisecondsDelay); , um zu warten, bis das Verzeichnis wirklich verschwunden ist
fuchs777
16

Eine wichtige Sache, die erwähnt werden sollte (ich hatte sie als Kommentar hinzugefügt, aber ich darf nicht), ist, dass sich das Verhalten der Überladung von .NET 3.5 auf .NET 4.0 geändert hat.

Directory.Delete(myPath, true);

Ab .NET 4.0 werden Dateien im Ordner selbst gelöscht, jedoch NICHT in 3.5. Dies ist auch in der MSDN-Dokumentation zu sehen.

.NET 4.0

Löscht das angegebene Verzeichnis und, falls angegeben, alle Unterverzeichnisse und Dateien im Verzeichnis.

.NET 3.5

Löscht ein leeres Verzeichnis und, falls angegeben, alle Unterverzeichnisse und Dateien im Verzeichnis.

Jettatore
quelle
3
Ich denke, es ist nur eine Dokumentationsänderung ... Wenn nur ein "leeres Verzeichnis" gelöscht wird, was würde es bedeuten, auch Dateien im Verzeichnis mit dem Parameter 2 ° zu löschen? Wenn es leer ist, gibt es keine Dateien ...
Pisu
Ich fürchte, Sie gehen davon aus, dass Sie falsch liegen. Ich habe dies gepostet, nachdem ich den Code mit beiden Framework-Versionen getestet habe. Das Löschen eines nicht leeren Ordners in 3.5 löst eine Ausnahme aus.
Jettatore
15

Ich hatte das gleiche Problem unter Delphi. Das Endergebnis war, dass meine eigene Anwendung das Verzeichnis sperrte, das ich löschen wollte. Irgendwie wurde das Verzeichnis gesperrt, als ich darauf schrieb (einige temporäre Dateien).

Der Haken 22 war, ich habe ein einfaches Änderungsverzeichnis gemacht vor dem Löschen für das übergeordnete habe.

Drejc
quelle
6
+1 Jetzt gibt es etwas, das msdn für Directory.Delete erwähnt!
Ruben Bartelink
3
Gibt es eine endgültige Lösung mit einem vollständigen Quellcodebeispiel?
Kiquenet
11

Ich bin überrascht, dass niemand an diese einfache nicht rekursive Methode gedacht hat, mit der Verzeichnisse mit schreibgeschützten Dateien gelöscht werden können, ohne dass das schreibgeschützte Attribut der einzelnen Dateien geändert werden muss.

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

(Ändern Sie ein wenig, um ein cmd-Fenster, das im gesamten Internet verfügbar ist, nicht vorübergehend auszulösen.)

Piyush Soni
quelle
Schön, mit uns zu teilen, aber wären Sie so freundlich, die Änderungen einzubeziehen, die erforderlich sind, um das Auslösen des cmd-Fensters zu verhindern, anstatt uns zu veranlassen, über das Internet danach zu suchen?
ThunderGr
Das funktioniert nicht. In der gleichen Situation, in der ich die Datei an einer Eingabeaufforderung oder im Explorer löschen kann, gibt die Verwendung dieses Codes zum Aufrufen von rmdir den Exit-Code 145 aus, der übersetzt "Das Verzeichnis ist nicht leer" bedeutet. Es lässt das Verzeichnis leer, aber auch noch vorhanden, genau wie Directory.Delete ("", true)
Kevin Coulombe
@ Kevin Coulombe, Humm ... Sind Sie sicher, dass Sie die Schalter / s / q verwenden?
Piyush Soni
1
@ KevinCoulombe: Ja, es müssen diese COM-Komponenten sein. Wenn ich ein einfaches altes C # durchlaufe, funktioniert es und löscht das Verzeichnis zusammen mit den darin enthaltenen Dateien (schreibgeschützt oder nicht schreibgeschützt).
Piyush Soni
5
Wenn Sie sich für das, was im Framework enthalten sein sollte, auf externe Komponenten verlassen, ist dies eine "weniger als ideale" Idee, da es nicht mehr portabel (oder schwieriger) ist. Was ist, wenn die Exe nicht da sind? Oder die Option / geändert? Wenn die Lösung von Jeremy Edwards funktioniert, sollte sie
meiner Meinung
11

Sie können den Fehler reproduzieren, indem Sie Folgendes ausführen:

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

Beim Versuch, das Verzeichnis 'b' zu löschen, wird die IOException "Das Verzeichnis ist nicht leer" ausgelöst. Das ist dumm, da wir gerade das Verzeichnis 'c' gelöscht haben.

Nach meinem Verständnis ist die Erklärung, dass das Verzeichnis 'c' als gelöscht gestempelt ist. Das Löschen ist jedoch noch nicht im System festgeschrieben. Das System hat eine Antwort, der Auftrag ist erledigt, während er tatsächlich noch verarbeitet wird. Das System wartet wahrscheinlich, bis sich der Datei-Explorer auf das übergeordnete Verzeichnis konzentriert hat, um das Löschen festzuschreiben.

Wenn Sie sich den Quellcode der Löschfunktion ( http://referencesource.microsoft.com/#mscorlib/system/io/directory.cs ) ansehen, werden Sie feststellen, dass die native Win32Native.RemoveDirectory-Funktion verwendet wird. Dieses Warteverhalten wird hier vermerkt:

Die Funktion RemoveDirectory markiert ein Verzeichnis zum Löschen beim Schließen. Daher wird das Verzeichnis erst entfernt, wenn das letzte Handle des Verzeichnisses geschlossen wird.

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

Schlaf und Wiederholung ist die Lösung. Vgl. Die Lösung des Ryascl.

Olivier de Rivoyre
quelle
8

Ich hatte diese seltsamen Berechtigungsprobleme beim Löschen von Benutzerprofilverzeichnissen (in C: \ Dokumente und Einstellungen), obwohl dies in der Explorer-Shell möglich war.

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

Es macht für mich keinen Sinn, was eine "Datei" -Operation in einem Verzeichnis macht, aber ich weiß, dass es funktioniert und das reicht mir!

p.campbell
quelle
2
Immer noch keine Hoffnung, wenn das Verzeichnis viele Dateien enthält und der Explorer den Ordner mit diesen Dateien öffnet.
sieht
3

Diese Antwort basiert auf: https://stackoverflow.com/a/1703799/184528 . Der Unterschied zu meinem Code besteht darin, dass wir nur dann viele Unterverzeichnisse und Dateien löschen, wenn dies erforderlich ist. Ein Aufruf von Directory.Delete schlägt beim ersten Versuch fehl (was daran liegen kann, dass der Windows Explorer ein Verzeichnis betrachtet).

    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
quelle
Wie soll der Ordner gelöscht werden, wenn es einen gab UnauthorizedAccessException? Es würde einfach wieder werfen. Und wieder. Und wieder ... Weil es jedes Mal zu gehen geht catchund die Funktion erneut aufruft. A Thread.Sleep(0);ändert Ihre Berechtigungen nicht. Es sollte nur den Fehler protokollieren und an diesem Punkt ordnungsgemäß fehlschlagen. Und diese Schleife wird nur so lange fortgesetzt, wie das (Unter-) Verzeichnis geöffnet ist - es wird nicht programmgesteuert geschlossen. Sind wir bereit, dies nur so lange zuzulassen, wie diese Dinge offen bleiben? Gibt es einen besseren Weg?
Vapcguy
Wenn es eine gibt UnauthorizedAccessException, wird manuell versucht, jede Datei manuell zu löschen. Es macht also weiterhin Fortschritte, indem es in die Verzeichnisstruktur übergeht. Ja, möglicherweise löst jede Datei und jedes Verzeichnis dieselbe Ausnahme aus. Dies kann jedoch auch einfach auftreten, weil der Explorer ein Handle daran hält (siehe stackoverflow.com/a/1703799/184528 ). Ich werde "tryAgain" in "secondTry" ändern. um es klarer zu machen.
Cdiggins
Um genauer zu antworten, wird "true" übergeben und ein anderer Codepfad ausgeführt.
Cdiggins
Richtig, habe deine Bearbeitung gesehen, aber mein Punkt ist nicht das Löschen von Dateien, sondern das Löschen des Verzeichnisses. Ich habe Code geschrieben, mit dem ich im Wesentlichen Process.Kill()jeden Prozess ausführen kann, durch den eine Datei gesperrt werden kann, und die Dateien löschen kann. Das Problem, auf das ich stoße, ist das Löschen eines Verzeichnisses, in dem eine dieser Dateien noch geöffnet war (siehe stackoverflow.com/questions/41841590/… ). Wenn Sie also diese Schleife durchlaufen, egal was sie sonst noch tut, schlägt Directory.Delete()sie immer noch fehl, wenn dieses Handle nicht freigegeben werden kann.
Vapcguy
Das Gleiche gilt für das UnauthorizedAccessExceptionLöschen von Dateien, da das Löschen von Dateien (vorausgesetzt, dies war sogar zulässig, da das Aufrufen dieses Codes fehlgeschlagen ist Directory.Delete()) Ihnen nicht auf magische Weise die Berechtigung zum Löschen des Verzeichnisses erteilt.
Vapcguy
3

Keine der oben genannten Lösungen hat bei mir gut funktioniert. Am Ende habe ich eine bearbeitete Version der @ryascl-Lösung wie folgt verwendet:

    /// <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
quelle
2

Ist es möglich, dass Sie eine Race-Bedingung haben, bei der ein anderer Thread oder Prozess dem Verzeichnis Dateien hinzufügt:

Die Reihenfolge wäre:

Prozess A löschen:

  1. Leeren Sie das Verzeichnis
  2. Löschen Sie das (jetzt leere) Verzeichnis.

Wenn jemand anderes eine Datei zwischen 1 und 2 hinzufügt, würde dann vielleicht 2 die aufgeführte Ausnahme auslösen?

Douglas Leeder
quelle
2

Ich habe einige Stunden damit verbracht, dieses Problem und andere Ausnahmen mit dem Löschen des Verzeichnisses zu lösen. Das ist meine Lösung

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

Dieser Code hat die kleine Verzögerung, die für meine Anwendung nicht wichtig ist. Aber seien Sie vorsichtig, die Verzögerung kann ein Problem für Sie sein, wenn Sie viele Unterverzeichnisse in dem Verzeichnis haben, das Sie löschen möchten.

Demid
quelle
8
-1 Worum geht es bei der Verzögerung? Bitte nicht zufällig programmieren!
Ruben Bartelink
1
@Ruben Ich habe nicht gesagt, dass du falsch liegst. Ich habe nur gesagt, dass es eine harte Strafe ist, es nur für dieses zu stimmen. Ich stimme Ihnen jedoch zu, dass die 4 Upvotes nicht zu 4 Downvotes geführt haben. Ich würde Ihren Kommentar ebenfalls positiv bewerten, aber ich würde die Antwort wegen einer ungeklärten Verzögerung nicht ablehnen :)
ThunderGr
1
@RubenBartelink und andere: Obwohl mir dieser Code nicht besonders gefällt (ich habe eine andere Lösung mit einem ähnlichen Ansatz veröffentlicht), ist die Verzögerung hier angemessen. Das Problem liegt höchstwahrscheinlich außerhalb der Kontrolle der App. Möglicherweise scannt eine andere App den FS regelmäßig neu und sperrt so den Ordner für kurze Zeit. Die Verzögerung löst das Problem, indem der Fehlerbericht auf Null heruntergezählt wird. Wen kümmert es, wenn wir keine verdammte Ahnung von der Grundursache haben?
Andrey Tarantsov
1
@RubenBartelink Wenn Sie darüber nachdenken, ist es hier eine verantwortungslose Lösung, beim Löschen von NTFS-Verzeichnissen keinen Delay-and-Retry-Ansatz zu verwenden. Jede Art von laufendem Durchlaufen von Dateien blockiert das Löschen, sodass es früher oder später fehlschlagen muss. Und Sie können nicht erwarten, dass alle Such-, Sicherungs-, Antiviren- und Dateiverwaltungstools von Drittanbietern nicht in Ihrem Ordner enthalten sind.
Andrey Tarantsov
1
@RubenBartelink Ein weiteres Beispiel ist, dass Sie eine Verzögerung von 100 ms angeben und die höchste Sperrzeit aller Software auf dem Ziel-PC die AV-Software = 90 ms ist. Angenommen, es gibt auch eine Sicherungssoftware, die Dateien für 70 ms sperrt. Jetzt sperrt der AV eine Datei, Ihre App wartet 100 ms, was normalerweise in Ordnung ist, stößt dann aber auf eine andere Sperre, da die Sicherungssoftware die Datei an der 70-ms-Marke des AV-Scans abruft und daher weitere 40 ms benötigt, um die Datei freizugeben. Während die AV-Software länger dauert und Ihre 100 ms normalerweise länger sind als eine der beiden Apps, müssen Sie dennoch berücksichtigen, wann sie in der Mitte startet.
Vapcguy
2

Das Löschen rekursiver Verzeichnisse, bei denen keine Dateien gelöscht werden, ist sicherlich unerwartet. Mein Fix dafür:

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

Ich habe Fälle erlebt, in denen dies geholfen hat, aber im Allgemeinen löscht Directory.Delete Dateien in Verzeichnissen beim rekursiven Löschen, wie in msdn dokumentiert .

Von Zeit zu Zeit stoße ich auch als Benutzer von Windows Explorer auf dieses unregelmäßige Verhalten: Manchmal kann ich einen Ordner nicht löschen (die unsinnige Meldung lautet "Zugriff verweigert"), aber wenn ich einen Drilldown durchführe und untere Elemente lösche, kann ich die oberen löschen Artikel auch. Ich denke, der obige Code befasst sich mit einer Betriebssystemanomalie - nicht mit einem Problem mit der Basisklassenbibliothek.

Stadtkind
quelle
1

Das Verzeichnis oder eine Datei darin ist gesperrt und kann nicht gelöscht werden. Finden Sie den Täter, der es sperrt, und prüfen Sie, ob Sie es beseitigen können.

Vilx-
quelle
T1000 an Benutzer mit geöffnetem Ordner: "Sie sind beendet!"
Vapcguy
1

Es scheint, dass die Auswahl des Pfads oder Unterordners im Windows Explorer ausreicht, um eine einzelne Ausführung von Directory.Delete (Pfad, true) zu blockieren, eine IOException wie oben beschrieben auszulösen und zu sterben, anstatt Windows Explorer in einen übergeordneten Ordner zu booten und als zu verfahren erwartet.

David Alpert
quelle
Dies scheint mein Problem gewesen zu sein. Sobald ich den Explorer geschlossen und wieder ausgeführt habe, keine Ausnahme. Selbst die Auswahl der Eltern der Eltern war nicht genug. Ich musste den Explorer tatsächlich schließen.
Scott Marlowe
Ja, das passiert und ist eine Ursache. Also eine Idee, wie man programmgesteuert damit umgeht, oder ist die Antwort nur immer, um sicherzustellen, dass alle 1000 Benutzer diesen Ordner geschlossen haben?
Vapcguy
1

Ich hatte heute dieses Problem. Es geschah, weil ich den Windows Explorer für das Verzeichnis geöffnet hatte, das gelöscht werden wollte, was dazu führte, dass der rekursive Aufruf fehlschlug und somit die IOException. Stellen Sie sicher, dass keine Handles für das Verzeichnis geöffnet sind.

Außerdem ist MSDN klar, dass Sie keine eigene Zusammenfassung schreiben müssen: http://msdn.microsoft.com/en-us/library/fxeahc5f.aspx

GrokSrc
quelle
1

Ich hatte das gleiche Problem mit Windows Workflow Foundation auf einem Build-Server mit TFS2012. Intern wird der Workflow Directory.Delete () mit dem rekursiven Flag auf true gesetzt. In unserem Fall scheint es netzwerkbezogen zu sein.

Wir haben einen binären Ablageordner auf einer Netzwerkfreigabe gelöscht, bevor wir ihn neu erstellt und mit den neuesten Binärdateien gefüllt haben. Jeder andere Build würde fehlschlagen. Beim Öffnen des Ablageordners nach einem fehlgeschlagenen Build war der Ordner leer, was darauf hinweist, dass alle Aspekte des Aufrufs von Directory.Delete () erfolgreich waren, mit Ausnahme des Löschens des tatsächlichen Verzeichnisses.

Das Problem scheint auf die Asynchronität der Netzwerkdateikommunikation zurückzuführen zu sein. Der Build-Server wies den Dateiserver an, alle Dateien zu löschen, und der Dateiserver meldete dies, obwohl dies noch nicht vollständig geschehen war. Dann forderte der Build-Server das Löschen des Verzeichnisses an, und der Dateiserver lehnte die Anforderung ab, da das Löschen der Dateien noch nicht vollständig abgeschlossen war.

Zwei mögliche Lösungen in unserem Fall:

  • Erstellen Sie die rekursive Löschung in unserem eigenen Code mit Verzögerungen und Überprüfungen zwischen den einzelnen Schritten
  • Versuchen Sie es nach einer IOException bis zu X Mal, und geben Sie eine Verzögerung an, bevor Sie es erneut versuchen

Die letztere Methode ist schnell und schmutzig, scheint aber den Trick zu tun.

Shaun
quelle
1

Dies liegt an FileChangesNotifications.

Es passiert seit ASP.NET 2.0. Wenn Sie einen Ordner in einer App löschen, wird dieser neu gestartet . Sie können es mithilfe von ASP.NET Health Monitoring selbst anzeigen .

Fügen Sie diesen Code einfach zu Ihrer web.config / configuration / system.web hinzu:

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


Nach dem Auschecken Windows Log -> Application. Was ist los:

Wenn Sie einen Ordner löschen und ein Unterordner vorhanden ist, wird zuerst der Unterordner gelöscht Delete(path, true). FileChangesMonitor muss über das Entfernen und Herunterfahren Ihrer App informiert sein. Inzwischen ist Ihr Hauptverzeichnis noch nicht gelöscht. Dies ist das Ereignis aus Log:


Geben Sie hier die Bildbeschreibung ein


Delete() hat seine Arbeit nicht beendet und da die App heruntergefahren wird, wird eine Ausnahme ausgelöst:

Geben Sie hier die Bildbeschreibung ein

Wenn sich in einem Ordner, den Sie löschen, keine Unterordner befinden, löscht Delete () nur alle Dateien und diesen Ordner. Die App wird ebenfalls neu gestartet, es treten jedoch keine Ausnahmen auf , da der Neustart der App nichts unterbricht. Trotzdem verlieren Sie alle in Bearbeitung befindlichen Sitzungen, die App reagiert beim Neustart nicht auf Anfragen usw.

Was jetzt?

Es gibt einige Problemumgehungen und Optimierungen, um dieses Verhalten zu deaktivieren: Directory Junction , FCN mit Registrierung deaktivieren , FileChangesMonitor mithilfe von Reflection stoppen (da es keine exponierte Methode gibt) , aber alle scheinen nicht richtig zu sein, da FCN für a vorhanden ist Grund. Es kümmert sich um die Struktur Ihrer App , die nicht die Struktur Ihrer Daten ist . Die kurze Antwort lautet: Platzieren Sie Ordner, die Sie löschen möchten, außerhalb Ihrer App. FileChangesMonitor erhält keine Benachrichtigungen und Ihre App wird nicht jedes Mal neu gestartet. Sie erhalten keine Ausnahmen. Um sie im Web sichtbar zu machen, gibt es zwei Möglichkeiten:

  1. Erstellen Sie einen Controller, der eingehende Anrufe verarbeitet und dann Dateien zurückgibt, indem Sie aus einem Ordner außerhalb einer App (außerhalb von wwwroot) lesen.

  2. Wenn Ihr Projekt groß ist und die Leistung am wichtigsten ist, richten Sie einen separaten kleinen und schnellen Webserver für die Bereitstellung statischer Inhalte ein. Auf diese Weise überlassen Sie IIS seinen spezifischen Job. Es kann sich auf demselben Computer (Mungo für Windows) oder einem anderen Computer (Nginx für Linux) befinden. Eine gute Nachricht ist, dass Sie keine zusätzliche Microsoft-Lizenz bezahlen müssen, um einen statischen Inhaltsserver unter Linux einzurichten.

Hoffe das hilft.

römisch
quelle
1

Wie oben erwähnt, schlägt die "akzeptierte" Lösung in Bezug auf Analysepunkte fehl - dennoch wird sie von den Leuten markiert (???). Es gibt eine viel kürzere Lösung, die die Funktionalität ordnungsgemäß repliziert:

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

Ich weiß, behandelt die später erwähnten Berechtigungsfälle nicht, aber in jeder Hinsicht bietet FAR BETTER die erwartete Funktionalität des ursprünglichen / stock Directory.Delete () - und mit viel weniger Code .

Sie können die Verarbeitung sicher fortsetzen, da das alte Verzeichnis nicht im Weg ist ... auch wenn es nicht weg ist, weil das 'Dateisystem immer noch aufholt' (oder was auch immer MS für die Bereitstellung einer fehlerhaften Funktion entschuldigt hat) .

Wenn Sie wissen, dass Ihr Zielverzeichnis groß / tief ist und nicht warten möchten (oder sich um Ausnahmen kümmern möchten), kann die letzte Zeile durch Folgendes ersetzt werden:

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

Sie können immer noch sicher weiterarbeiten.

rauben
quelle
2
Kann Ihre Zuweisung vereinfacht werden durch: string tfilename = Path.Combine (Path.GetDirectoryName (Ziel), Path.GetRandomFileName ());
Pete
1
Ich muss Pete zustimmen. Der geschriebene Code fügt das Trennzeichen nicht hinzu. Es nahm meinen Weg \\server\C$\dirund machte es \\server\C$asf.yuw. Als Ergebnis habe ich eine Fehlermeldung erhalten: Directory.Move()- Source and destination path must have identical roots. Move will not work across volumes. Hat einwandfrei funktioniert, nachdem ich Petes Code verwendet habe, AUSSER es werden keine Handles für gesperrte Dateien oder offene Verzeichnisse verwendet, sodass der ThreadPoolBefehl nie aufgerufen wird .
Vapcguy
VORSICHT: Diese Antwort sollte nur mit rekursiv = wahr verwendet werden. Wenn false, wird das Verzeichnis verschoben, auch wenn es nicht leer ist. Welches wäre ein Fehler; In diesem Fall besteht das richtige Verhalten darin, eine Ausnahme auszulösen und das Verzeichnis so zu belassen, wie es war.
ToolmakerSteve
1

Dieses Problem kann unter Windows auftreten, wenn sich Dateien in einem Verzeichnis (oder einem beliebigen Unterverzeichnis) befinden, dessen Pfadlänge mehr als 260 Symbole beträgt.

In solchen Fällen müssen Sie \\\\?\C:\mydirstatt löschen C:\mydir. Über das Limit von 260 Symbolen können Sie hier lesen .

HostageBrain
quelle
1

Ich habe mit dieser tausendjährigen Technik gelöst (Sie können den Thread.Sleep alleine im Fang lassen)

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
quelle
0

Wenn das aktuelle Verzeichnis Ihrer Anwendung (oder einer anderen Anwendung) dasjenige ist, das Sie löschen möchten, handelt es sich nicht um einen Zugriffsverletzungsfehler, aber ein Verzeichnis ist nicht leer. Stellen Sie sicher, dass es sich nicht um Ihre eigene Anwendung handelt, indem Sie das aktuelle Verzeichnis ändern. Stellen Sie außerdem sicher, dass das Verzeichnis in keinem anderen Programm geöffnet ist (z. B. Word, Excel, Total Commander usw.). Die meisten Programme werden in das Verzeichnis der zuletzt geöffneten Datei verschoben, was dazu führen würde.

Konfigurator
quelle
0

Bei Netzwerkdateien kann Directory.DeleteHelper (rekursiv: = true) eine IOException verursachen, die durch die Verzögerung beim Löschen der Datei verursacht wird

überfüllt
quelle
0

Ich denke, dass es eine Datei gibt, die von einem Stream geöffnet ist, von dem Sie nicht wissen, dass ich das gleiche Problem hatte, und das gelöst wurde, indem alle Streams geschlossen wurden, die auf das Verzeichnis verweisen, das ich löschen wollte.

Ahmad Moussa
quelle
0

Dieser Fehler tritt auf, wenn eine Datei oder ein Verzeichnis als verwendet verwendet wird. Es ist ein irreführender Fehler. Überprüfen Sie, ob Explorer- oder Befehlszeilenfenster für ein Verzeichnis in der Baumstruktur oder ein Programm, das eine Datei in dieser Baumstruktur verwendet, geöffnet sind.

allen1
quelle
0

Ich habe eine mögliche Instanz des angegebenen Problems gelöst, als die Methoden asynchron und wie folgt codiert waren:

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

Mit diesem:

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

Fazit? Es gibt einen asynchronen Aspekt beim Entfernen des Handles, mit dem die Existenz überprüft wird, mit dem Microsoft nicht sprechen konnte. Es ist, als ob die asynchrone Methode in einer if-Anweisung die if-Anweisung hat, die sich wie eine using-Anweisung verhält.

Pat Pattillo
quelle
0

Sie müssen keine zusätzliche Methode für die Rekursivität erstellen oder Dateien im Ordner extra löschen. Dies alles geschieht automatisch durch einen Anruf

DirectoryInfo.Delete ();

Details finden Sie hier .

So etwas funktioniert ganz gut:

  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
    }

Wenn Sie true als Variable an die Löschmethode übergeben, werden auch Unterdateien und Unterordner mit Dateien gelöscht.

nzrytmn
quelle
-2

Keine der oben genannten Antworten hat bei mir funktioniert. Es scheint, dass meine eigene App verwendetDirectoryInfo im Zielverzeichnis dazu geführt hat, dass sie gesperrt blieb.

Das Erzwingen der Speicherbereinigung schien das Problem zu beheben, jedoch nicht sofort. Bei Bedarf einige Löschversuche.

Beachten Directory.ExistsSie, dass es nach einer Ausnahme verschwinden kann. Ich weiß nicht, warum sich das Löschen für mich verzögert hat (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
quelle
1
-1 Zufällige Programmierung. Welches Objekt macht was, wenn GC'd? Ist das in irgendeiner Weise ein guter allgemeiner Rat? (Ich glaube Ihnen, wenn Sie sagen, Sie hatten ein Problem und dass Sie diesen Code verwendet haben und das Gefühl haben, dass Sie jetzt kein Problem haben, aber das ist einfach nicht der Punkt)
Ruben Bartelink
@ RubenBartelink Ich stimme zu. Es ist ein Hack. Voodoo-Code, der etwas bewirkt, wenn nicht klar ist, was er löst oder wie. Ich würde eine richtige Lösung lieben.
Reactgular
1
Mein Problem ist, dass alles, was über stackoverflow.com/a/14933880/11635 hinaus hinzugefügt wird, höchst spekulativ ist. Wenn ich könnte, würde ich zufällig eine -1 für die Vervielfältigung und eine -1 für Spekulation / Programmierung geben. Das Besprühen GC.Collectist a) nur ein schlechter Rat und b) keine ausreichend häufige allgemeine Ursache für gesperrte Verzeichnisse, um hier aufgenommen zu werden. Wählen Sie einfach einen der anderen aus und säen Sie unschuldigen Lesern keine Verwirrung
Ruben Bartelink
3
Verwenden Sie GC.WaitForPendingFinalizers (); nach GC.Collect (); Dies wird wie erwartet funktionieren.
Heiner
Nicht sicher, ungetestet, aber vielleicht besser wäre es, etwas mit einer usingAussage zu tun , dann: 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

addiere true im zweiten Parameter.

Directory.Delete(path, true);

Es wird alles entfernen.

Santiago Medina Chaverra
quelle
1
Directory.Delete (Pfad, true); war die ursprüngliche Frage
Pisu