Delete Directory and all of its subdirectories and files uncondionally !

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 10 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    Delete Directory and all of its subdirectories and files uncondionally !

    Hi,

    es sollte eigentlich ganz einfach zu lösen sein:

    Ich möchte ein Directory löschen, mitsamt allen enthaltenen Files und Subdirectories ... auch wenn einzelne Files "read only" sind. Also ohne wenn und aber.

    Ich habe verschiedene Dinge ausprobiert. Etwa:

    VB.NET-Quellcode

    1. IO.Directory.Delete(RealPath, recursive:=True)


    VB.NET-Quellcode

    1. My.Computer.FileSystem.DeleteDirectory(RealPath, FileIO.DeleteDirectoryOption.DeleteAllContents)


    Abgesehen davon, dass die zweite Lösung einen Imports Microsoft.VisualBasic verlangt, brechen alle Versuche ab, wenn irgendwo im Verzeichnisbaum "read only" files vorhanden sind.

    Ich hab im Internet nun schon sehr lange gesucht. Das Ding ist im Netz ein Dauerbrenner seit vielen Jahren !

    Aber die einzige Lösung, die zu funktionieren scheint, sind Schleifen, die den Baum "rekursiv" durchlaufen und die Files "per Hand" löschen.

    Das kann doch nicht die Wahrheit sein ! Nach 10 Jahren Diskussion über dieses Thema sollte es doch eine einfachere Lösung geben. Oder irre ich mich da und muss das wirklich "zu Fuß" erledigen!

    Vielleicht weiß jemand ja etwas Besseres ...

    LG
    Peter

    Peter329 schrieb:

    den Baum "rekursiv" durchlaufen und die Files "per Hand" löschen.
    Jede in sich geschlossene Lösung muss intern rekursiv arbeiten, denn Du musst ja in jedes Verzeichnis einzeln rein und nachsehen, ob dort noch ein weiteres Verzeichnis existiert oder gar ReadOnly-Files existieren. Genau das ist ja der Sinn des ReadOnly-Attributs, dass nicht Dateien / Verzeichnisse damit zufällig gelöscht werden.
    Natürlich kannst Du mit der VB-Variante arbeiten, nachdem Du vorher, ebenfalls rekursiv, die ReadOnly-Attribute zurückgesetzt hast.
    Oder Du siehst Dir die VB-Variante mit IlSpy an und machst das so, wie Du es brauchst.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Na gut, ich wollte ja auch nur wissen, ob ich nicht vielleicht zu blöd bin und eine naheliegende Lösung übersehe.

    Wenn das also nicht anders geht, als die ReadOnly Flags einzeln zurückzusetzen, dann hier mein Lösungsvorschlag:

    VB.NET-Quellcode

    1. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    2. 'Aufruf der Prozedur
    3. lblMessage.Text = DeleteDirUncond("C:\Test")
    4. End Sub
    5. Private Function DeleteDirUncond(myDirectory As String) As String
    6. 'Check existence of directory
    7. If Not Directory.Exists(myDirectory) Then Return "Directory does not exist"
    8. 'Reset read only flags
    9. Dim retVal As String = ResetReadOnlyFlags(myDirectory )
    10. If retVal <> "" Then Return retVal & " - directory kept" 'Cannot access complete tree - abort delete
    11. 'Delete directory
    12. Try
    13. IO.Directory.Delete(myDirectory, recursive:=True)
    14. Catch ex As Exception
    15. MessageBox.Show(ex.Message, "Delete Error")
    16. Return "Delete error" 'Cannot delete directory
    17. End Try
    18. Return "OK, Deleted" 'Directory deleted
    19. End Function
    20. Private Function ResetReadOnlyFlags(myDirectory1 As String) As String
    21. 'Reset directory read only flag
    22. Dim di As New DirectoryInfo(myDirectory1)
    23. di.Attributes = di.Attributes And Not FileAttributes.ReadOnly
    24. 'Get all entries
    25. Dim RealEntryList As String()
    26. Try
    27. RealEntryList = Directory.GetFileSystemEntries(myDirectory1)
    28. Catch ex As Exception
    29. MessageBox.Show(ex.Message, "Access Error")
    30. Return "Access error" 'Cannot access a directory
    31. End Try
    32. 'Process all entries
    33. For Each RealEntry In RealEntryList
    34. If CBool(File.GetAttributes(RealEntry) And FileAttributes.Directory) Then
    35. Dim retVal As String = ResetReadOnlyFlags(RealEntry) 'Recursive call for directories
    36. If retVal <> "" Then Return retVal 'Terminate recursion
    37. Else
    38. Dim fi As New FileInfo(RealEntry) 'Process files
    39. If fi.IsReadOnly Then fi.IsReadOnly = False 'Reset read only flag
    40. End If
    41. Next
    42. Return "" 'Reset read only flags successfully ended
    43. End Function


    Ich könnte mir vorstellen, dass andere genau dieses Problem haben. Vielleicht hilft der Code ja dem einen oder anderen ... Kritik oder Verbesserungsvorschläge sind natürlich willkommen !

    LG
    Peter

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „Peter329“ ()

    Hallo @Peter329

    Ich hätte schon einen Vorschlag, habs im Temp-Ordner kurz ausprobiert, jedoch die Addributte für das ändern der "Reandonly" bzw. für das Prüfen (bei dir die Zeile 25) der Dateien und Ordner habe ich auch noch nicht angepasst. Das müsste man noch machen.

    Hier werrden alle Dateien und Ordner in eine Liste geaddet, und nachher Sortiert. So sollte das Löschen kein Problem sein.


    VB.NET-Quellcode

    1. Private Sub Sample()
    2. Dim sTempPath = Path.GetTempPath
    3. Dim lstDir As New List(Of DirectoryInfo), lstFile As New List(Of FileInfo)
    4. Dim pathArr() As DirectoryInfo = {New DirectoryInfo(sTempPath), _
    5. New DirectoryInfo(sTempPath.Substring(0, sTempPath.Length - 1) & "2\")}
    6. For Each di As DirectoryInfo In pathArr
    7. If di.Exists Then
    8. If getDirFile(di, lstDir, lstFile) Then
    9. If MessageFunc(di.FullName) Then
    10. lstDir.Sort(Function(A As DirectoryInfo, B As DirectoryInfo)
    11. Return B.FullName.Length.CompareTo(A.FullName.Length)
    12. End Function)
    13. For Each f As FileInfo In lstFile
    14. Try
    15. f.Delete()
    16. Catch ex As Exception
    17. End Try
    18. Next
    19. For Each d As DirectoryInfo In lstDir
    20. Try
    21. d.Delete()
    22. Catch ex As Exception
    23. End Try
    24. Next
    25. End If
    26. End If
    27. End If
    28. Next
    29. End Sub
    30. Private Function getDirFile(ByVal strLp As DirectoryInfo, ByRef lstD As List(Of DirectoryInfo), ByVal lstF As List(Of FileInfo)) As Boolean
    31. Dim dr As New IO.DirectoryInfo(strLp.FullName)
    32. Dim lstFiles As New List(Of FileInfo)(10)
    33. getDirFile = False
    34. For Each fi As FileInfo In dr.GetFiles
    35. lstFiles.Add(fi)
    36. Next
    37. Dim lst = lstF.Union(lstFiles).Distinct.ToList
    38. lstF.Clear()
    39. lstF.AddRange(lst.Distinct)
    40. For Each diS As DirectoryInfo In dr.GetDirectories
    41. lstD.Add(diS)
    42. getDirFile(diS, lstD, lstF)
    43. Next
    44. Return True
    45. End Function


    VB.NET-Quellcode

    1. Private Function MessageFunc(ByVal p As String) As Boolean
    2. Dim dr As DialogResult = MessageBox.Show("Delete all Files and Directories?" & vbCrLf & p, _
    3. "Info", _
    4. MessageBoxButtons.YesNo, _
    5. MessageBoxIcon.Question)
    6. If dr = DialogResult.Yes Then
    7. Return True
    8. End If
    9. Return False
    10. End Function


    Freundliche Grüsse

    exc-jdbi

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „exc-jdbi“ ()

    @Peter329 Dein Umgang mit Enums ist verbesserungswürdig:

    VB.NET-Quellcode

    1. If di.Attributes.HasFlag(FileAttributes.ReadOnly) Then

    VB.NET-Quellcode

    1. If File.GetAttributes(RealEntry).HasFlag(FileAttributes.Directory) Then
    Dann würde ich keinen String als Rückgabewert nehmen, sondern auch ein Enum, das testet sich leichter.
    Und ne MessageBox hat in der tiefen Arbeitsroutine auch nix zu suchen, zumal die Message und der Rückgabetext identisch sind.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Okie dokie ... das passe ich an.

    Jetzt habe ich noch eine Frage : mit der.Hasflag Methode kann ich die Eigenschaft FileAttributes.ReadOnly abfragen.

    Aber wie setze ich die Eigenschaft ? Mit anderen Worten, was sollte ich statt

    VB.NET-Quellcode

    1. di.Attributes = di.Attributes And Not FileAttributes.ReadOnly


    kodieren.

    So klappt das leider nicht:

    VB.NET-Quellcode

    1. di.Attributes.HasFlag(FileAttributes.ReadOnly) = False

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Peter329“ ()

    @exc-jdbi so nicht!
    Die anderen Flags sollen doch nicht geändert werden!
    @Peter329 So:

    VB.NET-Quellcode

    1. di.Attributes = di.Attributes Or FileAttributes.Archive ' Setzen
    2. di.Attributes = di.Attributes And Not FileAttributes.Archive ' Löschen

    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Kurzer Einwurf, da nicht rein VB.Net. Ich komm mir damit zwar vor wie Harry Potter, der nen Bezoar statt nem Gegengiftzaubertrank im Unterricht zeigt, aber:

    VB.NET-Quellcode

    1. Dim p As Process = New Process()
    2. Dim pi As ProcessStartInfo = New ProcessStartInfo()
    3. pi.Arguments = " /C rd /s /q " & Verzeichnisname
    4. pi.FileName = "cmd.exe"
    5. p.StartInfo = pi
    6. p.Start()

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    @RFG

    Jau, wie man die Bit Schalter mit OR und NOT AND setzt, das ist mir schon klar. Ich wollte ja nur sicher stellen, das mein Umgang mit den ENUMs jetzt dein Wohlgefallen findet. :)

    Das ARCHIVE Attribut ist hier übrigens vollkommen irrelevant. So muss der Code jetzt "richtig" lauten:

    VB.NET-Quellcode

    1. 'Process directory
    2. Dim di As New DirectoryInfo(myDirectory1)
    3. If di.Attributes.HasFlag(FileAttributes.ReadOnly) Then _
    4. di.Attributes = di.Attributes And Not FileAttributes.ReadOnly 'Reset directory read only flag


    @VaporiZed

    Jau, mit einem DOS command kann man einen Verzeichnisbaum auch wegputzen, selbst wenn er ReadOnly Directories oder ReadOnly Files enthält. Nur p.start() läuft asynchron. Und das ist hier nicht zulässig, da mein Programm erst weiter arbeiten darf, wenn der Delete durch ist.

    Natürlich kann ich mir jetzt mehr oder weniger "schweinische" Techniken vorstellen, wie man das trotzdem hinkriegen kann ... aber ich möche halt ein "hübsches" Programm schreiben, dass nur ordentliche .NET Konstrukte verwendet. :)

    @all

    Mein Programm läuft jetzt zuverlässig und schnell auch bei großen Datenmengen. Wenn Directories nicht zugreifbar sind oder Files gelockt sind, bricht der Befehl mit einer "vernünftigen" Fehlermeldung ab. Ich bin mit der Lösung soweit sehr zufrieden.

    Vielen Dank an die Ratgeber!
    LG
    Peter

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Peter329“ ()

    @Peter329 Jou, HasFlag(...) ist da eine feine Sache.
    Diese Zeile ohne If reicht auch völlig, allerdings könnte das bei mehreren hunderttausend Dateien länger dauern, was ich wiederum nicht glaube, da das System hier sehr intelligent ist und das selbst abfragt.

    VB.NET-Quellcode

    1. di.Attributes = di.Attributes And Not FileAttributes.ReadOnly 'Reset directory read only flag

    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!