Probleme beim umbenennen von Ordner (Bibliothek)

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

Es gibt 23 Antworten in diesem Thema. Der letzte Beitrag () ist von kafffee.

    Probleme beim umbenennen von Ordner (Bibliothek)

    Guten Abend allerseits!

    Bin grad zufällig auf ein doch sehr schwer wiegendes Problem gestossen:

    Ich versuche einen Ordner umzubenennen:

    VB.NET-Quellcode

    1. Try
    2. Dim OrdnerInfo As New System.IO.DirectoryInfo(AktuellerOrdner)
    3. Dim ParentVerzeichnis As String = OrdnerInfo.Parent.FullName
    4. System.IO.Directory.Move(AktuellerOrdner, ParentVerzeichnis & "\" & NeuerOrdnerName)
    5. AktuellerOrdner = ParentVerzeichnis & "\" & NeuerOrdnerName
    6. Catch ex As Exception
    7. Dim OKVM = New OKDialogViewModel
    8. OKVM.Meldung = "Der Ordner konnte nicht umbenannt werden. " & Environment.NewLine & "Fehlermeldung: " & ex.Message
    9. dialogService.ShowModalDialog("", OKVM, Me, True, False, Services.WindowStyle.None, Services.ResizeMode.NoResize, 500, Services.SizeToContent.Height, Services.WindowStartupLocation.CenterOwner, "")
    10. End Try


    Und zwar tritt das Problem auf, wenn AktuellerOrdner zufällig der Pfad des Desktops ist (z.B. C:\Users\<User>\Desktop).


    Dann greift der Code unter Catch und es kommt die Fehlermeldung wie im Screenshot bereitsvorhanden.png. Wenn ich das nochmal mit dem gleichen (neuen) Ordnernamen mache kommt die Meldung wie im Screenshot nichtgefunden.png. Wenn ich dann den Ordner im Windows Explorer öffnen will, kommt der Screenshot nichtverfügbar.png.

    Nach einem Neustart des Computers ist dann der komplette Desktop leer, bis auf den Papierkorb, es fehlen sämtliche Dateien und Ordner...
    Nun die Frage:

    1. Wo finde ich meine Dateien und Ordner wieder? Die müssen ja noch irgendwo sein, sonst käme die Meldung wie in Screenshot bereitsvorhanden.png nicht, oder?
    2. Wie kann ich das unterbinden, dass Systemordner wie der Desktop umbenannt werden?
    Bilder
    • bereitsvorhanden.PNG

      32,39 kB, 1.053×385, 87 mal angesehen
    • nichtgefunden.PNG

      31,81 kB, 1.051×382, 84 mal angesehen
    • nichtverfügbar.PNG

      16,87 kB, 713×247, 99 mal angesehen

    kafffee schrieb:

    . Wo finde ich meine Dateien und Ordner wieder? Die müssen ja noch irgendwo sein, sonst käme die Meldung wie in Screenshot bereitsvorhanden.png nicht, oder?


    Da der Desktop Ordner ja wohl nicht umbenannt werden kann, werden wohl nur die Dateien verschoben worden sein. Lass dir den Pfad ausgeben den du als Ziel für Move angibst, dort solltest du die Dateien finden. Kannst ihn einfach in die Adressleiste kopieren und Enter drücken, spart das navigieren. Sollte der Zielordner nicht existieren, was ich mir zwar nicht vorstellen kann, Rechner abwürgen(ausknopf am PC halten) damit die Daten falls gelöscht nicht überschrieben werden und die Festplatte an einem anderen PC anschliessen und mit Recuva versuchen die Daten wieder herzustellen. Aber da das FW(bzw. NET) nahezu "Idiotensicher" ist, kann ich mir nicht vorstellen das die Daten nicht mehr da sind.

    Um das küftig zu unterbinden, hohl dir mit Environment.GetFolderPath alle SpecialFolders
    learn.microsoft.com/en-us/dotn…etfolderpath?view=net-8.0
    learn.microsoft.com/en-us/dotn…pecialfolder?view=net-8.0

    Und schau ob der zu verschiebende Ordner dabei ist.
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D

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

    @DTF

    OK das ist ne Idee. Die Daten waren nicht wichtig, den Desktop benutz ich eigentlich nur zur "Zwischenablage". Ich werd deinen Tipp trotzdem mal ausprobieren, wäre nämlich trotzdem gut zu wissen.

    Viel wichtiger wäre: Wie mach ich meinen Code idiotensicher? Gibt's da vielleicht ein Ordner-Attribut, der ihn als "Systemordner", wenn das der richtige Begriff dafür ist, kennzeichnet? Oder muss ich da manuell überprüfen, ob AktuellerOrdner vielleicht den gleichen Pfad hat wie mein Desktop oder eine Bibliothek.

    Mich wunderts halt auch, dass das Ganze von der .Move-Methode überhaupt zugelassen wird und nicht vor(!) dem Umbenennen einen Fehler wirft...

    Edit @DTF:

    Ah jetzt ham sich unsere Antworten überschnitten. Also du weisst nichts von einem solchen Ordner Attribut, weil es gibt evtl. noch andere Systemordner, wo das zum Problem werden könnte?

    kafffee schrieb:

    Also du weisst nichts von einem solchen Ordner Attribut, weil es gibt evtl. noch andere Systemordner, wo das zum Problem werden könnte?


    Also ich bin mir grad nicht ganz sicher, also alles was von Windows vorgegeben ist wie Documents, Users etc. da könnte das dann wohl auch passieren.

    Evtl. solltest du auch via FileAttribute testen können ob es ein SystemDir ist. Sie dir mal diese Enumeration an.
    learn.microsoft.com/en-us/dotn…leattributes?view=net-8.0

    Evtl. kommt hier das gleiche bei raus, sollte der Desktop nicht das Attribute System innehaben, probier es damit:
    learn.microsoft.com/en-us/wind…lwapi-pathissystemfoldera

    Wobei ich denke das es aufs selbse hinausläuft.

    C#-Quellcode

    1. FileAttributes attributes = File.GetAttributes("c:/Temp/testfile.txt");
    2. if ((attributes & FileAttributes.System) == FileAttributes.System)
    3. {
    4. -> Ist ein SystemDir
    5. }

    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D
    @DTF

    Hab mir grad nen kleines Testprogramm geschrieben, also wenn ich deinen Code nehme, dann ist z.B. das Desktop kein Systemverzeichnis, wohl aber z.B. C:\ oder C:\Programme\...

    Jetzt würde ich gern noch deinen zweiten Link ausprobieren, aber ich seh grad das ist C++... Wie benutze ich denn das in VB, das muss ich glaube ich ein Interface schreiben? Wollte schon immer mal wissen wie das geht, das ist jetzt die optimale Gelegenheit an einem einfachen Beispiel zum Üben :)

    Das würde ich dann auch noch in mein Testprogramm implementieren und es dann hier hochladen :)

    kafffee schrieb:

    das ist jetzt die optimale Gelegenheit an einem einfachen Beispiel zum Üben


    Kann man einfach nennen, aber es hángt von den Umständen ab. C++ Windows-Desktop Entwicklungskenntnisse auf jeden Fall von Vorteil.

    Via Platform-Invoke geht das(DllImport). Pinvoke solltest du schon mal gelesen haben. Wenn du Glück hast geht die Seite pinvoke.net wieder(spinnt in letzter Zeit) und du kannst die Deklaration kopieren.

    Geht hier schon mal los mit den Standart-Typen:
    learn.microsoft.com/en-us/wind…inprog/windows-data-types


    Bitbreiten sind immer zu beachten(Byte, Short, int .....) sonst gibt eine Exception, Ranges auch, HRESULT ist zwar eigendlich unsigned, kann aber trotzdem -1 sein, also immer bei fehlern in die Doku schauen, Marshal sollte dir auch nicht unbekannt sein. Oft braucht man das MarshallAs-Attribute, hier auch für den 1. Parameter. Beachte das wenn die Funktionsnamen mit A enden wird Ansi für strings genutzt, wenn die mit W enden Unicode. Gibst du kein A oder W am ende an, wird der Systemstandart verwendet. Der Funktionsname muss nicht zwingend mit dem originalen übereinstimmen, aber sollte der nicht gleich sein, musst du den EntryPoint in den Attributen angeben, sonst wird die Funktion nicht gefunden.

    Welche DLL du verwenden musst, findest du in der Doku ganz unten, sieh mal nach.
    Damit du auch was zu tun hast nur ein wenig PseudoCode:

    C#-Quellcode

    1. [DllImport("???????.dll")]
    2. private static extern bool PathIsSystemFolder([MarshalAs(UnmanagedType.???????)] string pszPath, ??????? dwAttrb);
    3. [DllImport("???????.dll")]
    4. private static extern bool PathIsSystemFolderA([MarshalAs(UnmanagedType.???????)] string pszPath, ??????? dwAttrb);
    5. [DllImport("???????.dll")]
    6. private static extern bool PathIsSystemFolderW([MarshalAs(UnmanagedType.???????)] string pszPath, ??????? dwAttrb);


    Hier jetzt nicht wichtig:
    Kommt auch vor das man besser einen Stringbuilder nutzt, aber das braucht man hier nicht. Kann auch sein das man speicher reservieren muss und mit marshal kopieren und dann den speicher wieder freigeben muss. Sollte HRESULT implicit konvertiert werden, auch das PreserveSig Attribute angeben.

    Von den 3 Pseudodeklarationen nimm einfach die erste(weil kein W oder A am Ende, somit System-Standart). Probier mal Intellisense mal durch welcher UnmanagedType funktioniert. kommen ja nicht viele in Frage.

    Manche funktionen bieten die möglichkeit wenn ein Fehler eintritt diesen als letzten Fehler einzutragen. Dann in den Attributen SetLastError auf true stellen, Dann kommnste mit GetLastError an den Fehlercode. Sollte aber auch via Marshal.GetLastWin32Error gehen. Ob eine Funktion den letzten Fehler so einträgt, steht immer in der Doku der Funktion und bei DllImport ob true oder false. Sollte da ein REFGUID stehen heist das eine Referenz einer GUID. Dann ByRef guid as Guid nutzen. So ist das auch mit anderen Typen.


    Ich glaub das wars im Groben, gibt noch mehr, aber da kannste nochmal explizit fragen bei Bedarf, für diese Funktion sollte das ausreichen.


    Diese Funktion gibt mir beim Desktop true aus, sollte bei dir auch so sein, wenn du es richtig machst.
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D

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

    @DTF

    Okay ich hab jetzt das hier:

    VB.NET-Quellcode

    1. <DllImport("Shlwapi.dll")>
    2. Private Shared Function PathIsSystemFolder(<MarshalAs(UnmanagedType.)) As Boolean
    3. End Function


    Auf PInvoke leider nichts gefunden. Wie komm ich jetzt auf die Datentypen der Argumente? Das hab ich noch nicht so ganz verstanden... IntelliSense bietet mir da schon eine ganze Stange an...
    Zumindest stimmt die übersetzung bis hier hin. Jetzt sehe ich auch du hast die Funktion als Shared deklariert, sehr gut. Hatte ich nicht erwähnt das die statisch sein müssen.

    DWORD ist nur als Plathalter gedacht, schau mal in die Doku welche Bitbreite das hat(Anzahl Bytes), dann nimmste einen (so viel vrerrate ich) ganzahligen Typen(Byte, Short, Int, Long......) mit der gleichen Bitbreite.

    In der Doku steht der erste Parameter ist ein LPCSTR, was ein Long Pointed Contant String(0 terminiert) ist, Intellisense könnte dir U8 vorschlagen, das wäre unsigned 8 Bytes. Also unsigned Long, I8 wäre signed 8 Bytes, also long. Nun aber haben wir was mit STR, probier mal durch welcher Type mit STR den passt. LPWSTR ist es in dieser kombination nicht(würde false beim desktop-pfad ausgeben). Gibt aber welche die ähnlich heissen.

    Um weitere verwirrung zu vermeiden, wegen des zweiten Parameters. Jenachdem was da denn für ein Typ verwendet wird, 0 oder Nothing reingeben, da wir ja pszPath angeben)
    The file attributes to be compared. Used only if pszPath is NULL.


    Fehlt nur noch hinter dem . der Typ für unmangedtype und DWORD muss mit dem richtigen ganzahligen Typen ersetzt werden.

    VB.NET-Quellcode

    1. <DllImport("Shlwapi.dll")>
    2. Private Shared Function PathIsSystemFolder(<MarshalAs(UnmanagedType.)> pszPath As String, dwAttrb As DWORD) As Boolean
    3. End Function

    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D

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

    Falsch.
    learn.microsoft.com/de-de/dotn…nmanagedtype?view=net-8.0

    AnsiBStr:
    Eine Zeichenfolge aus ANSI-Zeichen, die ein Einzelbyte mit Längenpräfix ist. Sie können diesen Member für den Datentyp String verwenden.


    LPCSTR ist 0-Terminiert. Also am Ende des Strings ist ein 0-Byte, wodurch man weiss, ah, hier ist das Ende. mit AnsiBStr sollte false kommen selbst wenn ein SystemDir, wenn das überhaupt fehlerfrei geht. Bei mir ist AnsiBStr garnicht verfügbar. Vermutlich wurde das entfernt, ich nutze NET8.

    Was hälst du von dem hier:?
    LPStr:
    Eine aus ANSI-Zeichen bestehende, mit NULL beendete Einzelbyte-Zeichenfolge. Sie können diesen Member für die Datentypen String und StringBuilder verwenden.


    Nun das "doppelwort" DoubleWORD
    DWORD
    A 32-bit unsigned integer. The range is 0 through 4294967295 decimal.
    This type is declared in IntSafe.h as follows:
    typedef unsigned long DWORD;


    Der native long hat nur 4 Bytes, der in NET/NetFX 8. Aber zumindest die Range von 0 bis 4294967295 ist abgedeckt. Nun könnte es knifflig sein zu verstehen, du kannst auch Integer nehmen, auch wenn da die Range von -2147483648 bis 2147483647 geht, das kann aber zu Problemem führen. Es werden einfach ja einfach Bytes als ganzzahl interpretiert. So kann es dann passieren das Werte nicht mit denen aus der Doku übereinstimmen. Ich habe festgestellt, das sehr sehr viele Integer für DWORD nehmen, finde ich aber nicht richtig.

    Führe diese beiden Zeilen mal aus und schau dir die Werte an:
    Debug.WriteLine(UInteger.MaxValue)
    Debug.WriteLine(Marshal.SizeOf(Of UInteger))

    Also ist das so 100% korrekt:

    VB.NET-Quellcode

    1. <DllImport("Shlwapi.dll")>
    2. Private Shared Function PathIsSystemFolder(<MarshalAs(UnmanagedType.LPStr)> pszPath As String, dwAttrb As UInteger) As Boolean
    3. End Function


    C#-Quellcode

    1. bool res = PathIsSystemFolder("pfad", 0);

    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „DTF“ ()

    OK, das stimmt. Aber das macht die runtime ganz gut. Bisher hatte ich mit BOOL und Boolean nie ein Problem. Anders sieht es bei den Zeichenkettenvariationen aus.
    Dann ist das so 100% korrekt:

    VB.NET-Quellcode

    1. <DllImport("Shlwapi.dll")>
    2. Private Shared Function PathIsSystemFolder(<MarshalAs(UnmanagedType.LPStr)> pszPath As String, dwAttrb As UInteger) As <MarshalAs(UnmanagedType.Bool)> Boolean
    3. End Function
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D
    Okay hab alles so gemacht, da kommt bei mir ein Laufzeitfehler beim Aufruf:

    Assistent für verwaltetes Debuggen "PInvokeStackImbalance"
    Nachricht = Assistent für verwaltetes Debuggen "PInvokeStackImbalance" : "Ein Aufruf an die PInvoke-Funktion "CheckIfSystemDirectory!CheckIfSystemDirectory.CPlusPlus::PathIsSystemFolder" hat das Gleichgewicht des Stapels gestört. Wahrscheinlich stimmt die verwaltete PInvoke-Signatur nicht mit der nicht verwalteten Zielsignatur überein. Überprüfen Sie, ob die Aufrufkonvention und die Parameter der PInvoke-Signatur mit der nicht verwalteten Zielsignatur übereinstimmen."
    Keine Ahnung welchen Code du jetzt verwendest, bei mir geht das einwandfrei. Egal ob Ansi oder Unicode. Du kannst ja mal die A und W Variante probieren. Möglich das dein System Unicodee verwendet und meins Ansi.

    VB.NET-Quellcode

    1. <DllImport("Shlwapi.dll")>
    2. Private Shared Function PathIsSystemFolder(<MarshalAs(UnmanagedType.LPStr)> pszPath As String, dwAttrb As UInteger) As <MarshalAs(UnmanagedType.Bool)> Boolean
    3. End Function
    4. <DllImport("Shlwapi.dll", CharSet:=CharSet.Ansi)>
    5. Private Shared Function PathIsSystemFolderA(<MarshalAs(UnmanagedType.LPStr)> pszPath As String, dwAttrb As UInteger) As <MarshalAs(UnmanagedType.Bool)> Boolean
    6. End Function
    7. <DllImport("Shlwapi.dll", CharSet:=CharSet.Unicode)>
    8. Private Shared Function PathIsSystemFolderW(<MarshalAs(UnmanagedType.LPWStr)> pszPath As String, dwAttrb As UInteger) As <MarshalAs(UnmanagedType.Bool)> Boolean
    9. End Function
    10. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    11. Dim desktop As String = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
    12. Dim documents As String = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
    13. Debug.WriteLine(PathIsSystemFolder(desktop, 0).ToString())
    14. Debug.WriteLine(PathIsSystemFolder(documents, 0).ToString())
    15. Debug.WriteLine(PathIsSystemFolder("D:/Filme", 0).ToString())
    16. Debug.WriteLine(PathIsSystemFolderA(desktop, 0).ToString())
    17. Debug.WriteLine(PathIsSystemFolderA(documents, 0).ToString())
    18. Debug.WriteLine(PathIsSystemFolderA("D:/Filme", 0).ToString())
    19. Debug.WriteLine(PathIsSystemFolderW(desktop, 0).ToString())
    20. Debug.WriteLine(PathIsSystemFolderW(documents, 0).ToString())
    21. Debug.WriteLine(PathIsSystemFolderW("D:/Filme", 0).ToString())
    22. End Sub


    Aber, da fällt mir noch was ein, du könntest den User fragen wenn es ein SystemDir ist, ob versucht werden soll den Ordner zu verschieben, ich hab auch Ordner mit PathMakeSystemFolder zu SystemOrdner gemacht.
    Bilder
    • Unbenannt.jpg

      649,11 kB, 1.804×833, 65 mal angesehen
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „DTF“ ()

    Ja ich probier das mal mit der andern Codierung.

    Gibt es denn keine Alternative zur Move-Methode? Ich find das bockelhart dass die sowas überhaupt zulässt. Ich muss das nochmal genauer untersuchen aber meine Dateien und Ordner hab ich bis jetzt noch nicht wiedergefunden, kann von Glück sagen dass es "nur" der Desktop war...

    Oder kann man vielleicht im Try-Block mit Finally was machen?

    kafffee schrieb:

    Gibt es denn keine Alternative zur Move-Methode?


    Nur kopieren. Das deine Dateien weg sind kann ich mir nicht vorstellen. MAch mal auf der Festplatte eine suche nach einer Datei die auf dem Desktop war mit * beginnend(*file.txt, *abc.vb). Ich hatte mal ein Slash zu wenig und hatte mich auch geweundert wo die Datei denn nun ist. War dann ein Ordner niedriger (C:/Ordner/Target/file.ext ->C:/Ordner/Targetfile.ext) mit den gewollten Ordnernamen+Dateiname als Dateiname.
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D
    @DTF

    Habs hinbekommen. Hatte übersehen dass ich beim zweiten Argument Long statt UInt drinhatte...

    Zu den "verlorenen" Dateien/Ordnern auf dem Desktop: Hab den Ordner "Ablage", den ich auf dem Desktop liegen hatte, mit der Windows-Explorer-Suche tatsächlich gefunden, und zwar, halt dich fest, unter C:\Benutzer\<Benutzer>\Desktop

    Auf dem Desktop wird dieser Ordner aber nicht angezeigt... Kann es sein, dass durch mein Programm, einfach nur der eingestellte Pfad für die Bibliothek Desktop geändert wurde? Wenn ich das mache:

    VB.NET-Quellcode

    1. Console.WriteLine("Desktop-Pfad: " & Environment.GetFolderPath(Environment.SpecialFolder.Desktop))


    wird auch C:\Benutzer\<Benutzer>\Desktop ausgegeben komischerweise...
    Ich kann dir nicht sagen was da passiert ist.

    Aber ist ja nun auch egal, du hast zumindest deine Daten wieder. Konnte mir auch nicht vorstellen das die endgültig weg sind.

    PS.
    @kafffee
    Danke, dir auch ein frohes neues.
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „DTF“ ()

    Hab jetzt mal in den Desktopeigenschaften nachgeschaut da gibts nen Button wiederherstellen. Da passiert aber auch nichts und der Pfad wird ja auch richtig angezeigt.

    Die Daten hab ich jetzt halt wieder weils nur eine Verknüpfung und ein Ordner war und ich deren Namen wusste.

    Hat jemand sonst noch eine Idee?

    PS @DTF
    Gutes Neues! :)

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „kafffee“ ()

    Was steht denn in den Eigenschaften Deines Desktops im Tab Pfade, wenn Du den "Desktop" mit der re. Maustaste anklickerst und dessen Eigenschaften aufrufst?
    Mit diesem Tab kannst alle Systemordner-Pfade umleiten, wenn Du deren Eigenschaften-Fenster öffnest.
    Ggf. den Ordner "Ablage" temporär in ein anderes Verzeichnis verschieben und dann zurück in den Desktop-Ordner schubsen.

    EDIT: Gibts in dem Desktop-Ordner noch die Datei "desktop.ini" mit dem Inhalt:

    VB.NET-Quellcode

    1. [.ShellClassInfo]
    2. LocalizedResourceName=@%SystemRoot%\system32\shell32.dll,-21769
    3. IconResource=%SystemRoot%\system32\imageres.dll,-183

    Bilder
    • 2024-02-09 01-33-25.jpg

      535,67 kB, 1.600×1.024, 35 mal angesehen