Voreinstellung BrowseForFolder Dialog

  • VB.NET

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

    Voreinstellung BrowseForFolder Dialog

    Hi,

    ich habe einen Dialog geschrieben, mit dem ich meine Files vom iPhone auf meinen PC hochlade (dabei habt ihr mir vor einiger Zeit geholfen!)

    So wie ich das verstanden habe, handelt es sich um einen COM Folder ... und den kann man mit den normalen .NET Methoden nicht in Zugriff nehmen (zB. Directory.GetLogicalDrives(), etc.).

    Stattdessen verwende ich den shell.BrowseForFolder() Dialog, um Zugriff auf die Files auf meinem iPhone zu erhalten.

    VB.NET-Quellcode

    1. Dim ssfDRIVES = &H11
    2. Dim shell = New ShellClass()
    3. Dim Hwnd As Integer
    4. Dim CellPhoneFolder =
    5. shell.BrowseForFolder(Hwnd, "Choose folder", 0, ssfDRIVES)


    Zunächst wird die Liste unter "ThisPC" angezeigt ... and dann muss ich "Apple iPhone" - "DCIM" - "OK" auswählen, um Zugriff auf den Ordner zu erhalten. Zur Illustration habe ich Screenshots angehängt.

    Das funktioniert soweit auch sehr gut. Aber mich nervt, dass ich jedesmal diese Auswahl erneut treffen muss. Ich würde gern DIREKT

    This PC - Apple iPhone - DCIM

    öffnen.

    Kann man den Startordner des BrowseForFolder Dialog nicht beim Öffnen einstellen ?

    Oder gibt es eine Möglichkeit das irgendwie programmtechnisch zu lösen ... so etwa wie in den .NET Dialogen, z.B. Directory.GetLogicalDrives().

    Ich weiß, COM Folder sind nicht ganz so leicht zu handhaben - aber ich würde mir diese nervige Pfad Auswahl schon ganz gern sparen. Wenn das nicht geht, dann bleibt halt alles so wie es ist ... Im Netz habe ich nichts passendes gefunden, aber vielleicht kennt von euch ja jemand eine bessere Technik.

    LG
    Peter
    Bilder
    • s 2024-02-07 13-55-169.jpg

      13,84 kB, 315×312, 16 mal angesehen
    • s 2024-02-07 13-56-139.jpg

      16,38 kB, 311×362, 20 mal angesehen

    Peter329 schrieb:

    Kann man den Startordner des BrowseForFolder Dialog nicht beim Öffnen einstellen ?
    Das Thema hatten wir doch alles schon und ich hatte auch schon ein Bsp. gezeigt wie man den Startordner sogar noch mit der alten API SHBrowseForFolder einstellen kann und ich hatte auch schon öffter Erwähnt das der Dialog per Shell.BrowseForFolder und die API SHBrowseForFolder veraltet sind. Nutze folgende Dialoge: OpenFileDialog, SaveFileDialog, PickFolderDialog (Single Select) per Interface IFileDialog verwenden wo Du den Startordner vorgeben kannst und Dateien und Ordner löschen, kopieren, verschieben und umbenennen per IFileOperation um die Dateien vom Handy auf den PC, und umgekehrt, zu kopieren.

    2 Möglichkeiten:
    Ordner vom Handy in einem Ordner auf den PC kopieren.
    PickFolderDialog (IFileDialog) -> IShellItem_Source
    PickFolderDialog (IFileDialog) -> IShellItem_Destination
    IFileOperation.CopyItem(IShellItem_Source, IShellItem_Destination)
    IFileOperation::PerformOperations

    Eine/Mehrere Dateien aus einem Ordner vom Handy in einem Ordner auf den PC kopieren.
    FileOpenDialog (IFileOpenDialog) -> IShellItemArray_Source
    PickFolderDialog (IFileDialog) -> IShellItem_Destination
    IFileOperation.CopyItems(IShellItemArray_Source, IShellItem_Destination)
    IFileOperation::PerformOperations

    Normalerweise speichern die neuen Dialoge den zuletzt gewählten Ordner automatisch so das dieser Ordner, beim erneuten Aufruf des Dialoges automatisch ausgewählt wird (sofern noch vorhanden). Du kannst für die Dialoge an verschiedenen Stellen einen Ordner vorgeben/festlegen. zb bei IFileDialog::AddPlace, IFileDialog::SetDefaultFolder oder auch bei IFileDialog::SetFolder. Du kannst auch für jeden Dialog eine eindeutige GUID festlegen -> IFileDialog::SetClientGuid unter dem der zuletzt ausgewählte Ordner gespeichert wird. Praktisch wenn man wie bei der ersten Möglichkeit den gleichen Dialog aufrufen möchte, aber unterschiedliche Ordner gespeichert werden sollen.

    Problematisch kann das aber für WPD-Geräte sein die per USB am PC angeschlossen werden. Der Pfad setzt sich ja aus ThisPC/DeviceID/StorageID/FolderName usw zusammen. Die DeviceID, bei gleichem WPD-Gerät, kann sich aber ändern wenn man einen anderen USB-Port verwendet. Könnte aber funktionieren wenn man den Pfad so angibt. Bsp: ThisPC/HandyName/InternerSpeicher/FolderName -> also so wie der Pfad im Explorer angezeigt wird.
    Mfg -Franky-

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

    -Franky- schrieb:

    Peter329 schrieb:

    Kann man den Startordner des BrowseForFolder Dialog nicht beim Öffnen einstellen ?
    Das Thema hatten wir doch alles schon


    Jau ... seit über zwei Jahren schlage ich mich mit diesem Problem herum ... ohne das wirklich völlig zufriedenstellend gebacken zu bekommen.

    Natürlich habe ich mir auch dein Beispielprojekt angesehen ... aber das ist halt recht komplex und will erst mal verstanden werden. Denn natürlich muss ich die Sache auf meine Bedürfnisse anpassen.

    Bisher verwende ich den BrowseForFolder Dialog um den DCIM Folder meines CellPhone zu erhalten. Wenn ich dieses COM-Object habe, dann kann ich darauf (mit einiger Programmlogik) die Files auf meinen PC kopieren. Um es klar zu sagen: ich verwende den BrowseForFolder Dialog nicht, weil ich den so toll finde oder weil ich beratungsresistent bin, sondern einfach deshalb, weil ich derzeit keine ALTERNATIVE habe ! Ich navigiere mit dem Dialog zum DCIM Folder und erhalte so das COM Objekt. Blöde ist halt nur, dass ich diesen Schritt jedesmal neu durchlaufen muss.

    Wenn ich jetzt dein Beispielprojekt verwende um diesen Pfad automatisch zu speichern, dann müsste ich wohl deinen "OpenFileDialog" verwenden. Aber wie erhalte ich denn damit das COM-Object für den DCIM Ordner? Wo ist das denn abgespeichert?

    Und natürlich möchte ich den Auswahdialog nach Möglichkeit nur durchlaufen, wenn der gespeicherte Pfad nicht gefunden wird. Ansonsten will ich direkt die Files hochladen ohne viel "Getüttel". Mit anderen Worten: ich müsste wohl an der Programmlogik einige Änderungen vornehmen. Wenn das überhaupt so funktionieren kann.

    Und noch etwas: die von @exc-jdbi vorgeschlagene Reparatur für die NullReference Exception klappt bei mir nicht:

    VB.NET-Quellcode

    1. ' ----==== Interface IFileDialog2 ====----
    2. Public Function Show() As Boolean
    3. Dim bolRet As Boolean = False
    4. m_DialogHwnd = IntPtr.Zero
    5. If m_FileOpenDialog IsNot Nothing Then
    6. 'If CType(m_FileOpenDialog, IFileDialog2).Show(Form.ActiveForm.Handle) = S_OK Then
    7. 'bolRet = True
    8. 'End If
    9. Dim activeformhandle = Form.ActiveForm.Handle
    10. If CType(Me.m_FileOpenDialog, IFileDialog2).Show(activeformhandle) = S_OK Then
    11. bolRet = True
    12. End If
    13. End If
    14. m_DialogHwnd = IntPtr.Zero
    15. Return bolRet
    16. End Function


    Form.ActiveForm.Handle ist nach wie vor "nothing" ... Ich verstehe auch gar nicht wie diese Reparatur funktionieren sollte, was sollte man denn durch das Umspeichern gewinnen ?

    Fragen über Fragen ... und natürlich will ich deine Freundlichkeit auch nicht überstrapazieren. Das ist schon ein zähes Thema ... na mal sehen, ob ich jetzt im dritten Anlauf den Dialog hinbekomme.

    LG
    Peter
    Bilder
    • s 2024-02-08 10-35-118.jpg

      62,35 kB, 1.397×348, 16 mal angesehen

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

    Das ist noch ganz lustig. Ich habe es gerade nochmals ausprobiert, und den Fehler kommt jetzt nicht mehr. So wie ich mich erinnern kann bin ich da schon auf das VS 2022 umgestiegen, und habe das auf FW 4.8 umgestellt. Ich habe mich natürlich auch gefragt, warum das es auf die eine Art funktioniert, und auf die andere Art nicht mehr. Nothing funkst bei mir aber immer.

    Wie auch immer jetzt funkst es auf jeden Fall, an einem anderen Tag vielleicht dann wieder nicht mehr. Warum das es so ist, ist sicher nicht gerade so auf die schnelle erklärbar.

    Ich habe mir aber auch schon die Überlegung gemacht, statt in diesem Projekt instanziert mit shared bzw. static Methoden zu arbeiten. Vielleicht ist das Ganze dann stabiler. Auf der anderen Seite ist es dann wahrscheinlich nicht mehr Threadsave oder sonst irgendwas.

    Freundliche Grüsse

    exc-jdbi

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

    exc-jdbi schrieb:

    Das ist noch ganz lustig.


    Na, lustig ist was anderes. Intermittierende Fehler sind so ziemlich das Dümmste was einem in der IT unterkommmen kann und das beflügelt nicht gerede mein Vertrauen, dass ich in den Code setze.. :)

    Aber Spaß beiseite: das mit der Exception macht mir eigentlich kein großes Kopfzerbrechen (auch wenn ich schon ganz gern wüsste, warum das Dingens da vereinzelt abbricht). Das kann man beheben (bzw. umgehen), etwa so so wie @franky es angeregt hat, beispielsweise durch einen zusätzlichen Parameter.

    Viel mehr beunruhig mich das hier:

    VB.NET-Quellcode

    1. Private Const IID_IOleWindow As String = "00000114-0000-0000-c000-000000000046"
    2. Private Const IID_IShellItem As String = "43826d1e-e718-42ee-bc55-a1e261c37bfe"
    3. Private Const IID_IFileDialog2 As String = "61744fc7-85b5-4791-a9b0-272276309b13"
    4. Private Const IID_IFileDialogEvents As String = "973510db-7d7f-452b-8975-74a85828d354"
    5. Private Const IID_IFileDialogCustomize As String = "e6fdd21a-163f-4975-9c8c-a69f1ba37034"
    6. Private Const IID_IFileDialogControlEvents As String = "36116642-d713-4b97-9b83-7484a9d00433"
    7. Private Const CLSID_FileOpenDialog As String = "dc1c5a9c-e88a-4dde-a5a1-60f82a20aef7"
    8. Private Const CLSID_FileSaveDialog As String = "c0b4e2f3-ba21-4773-8dba-335ec946eb8b"


    Ich frage mich, wie beständig diese Konstanten sind. Können die sich mit der nächsten Version von Windows ändern? Mit dem nächsten Wartungslevel? Mit dem nächsten Windows Update?

    Und was ist, wenn die Dinger eines Tages nicht mehr funktionieren? Wo bekomme ich denn dann die neuen Werte her? Wenn es die überhaupt gibt! Meine ehemaligen Ratgeber hier im Forum sind dann möglicherweise nicht mehr zur Stelle.

    Also da würde ich schon gern noch eure Meinung dazu hören, ehe ich meine funktionierende Lösung durch das neue Coding ersetze.

    LG
    Peter
    Das sind COM-Interface-Konstanten, wenn ich mich nicht täusche, und meiner Meinung nach würde es keinen Sinn machen, wenn die ändern.

    Aber ich halte da mich raus, weil WIR haben alle schon die Erfahrung gemacht, dass genau solche Sachen dann trotzdem, aus Gründen (z.b. Abwärtskompatibilität etc.) die vielleicht nicht einmal wirklich mehr nachvollziehbar sind geändert, umstrukturiert oder weiss was auch immer was gemacht wird.

    Freundliche Grüsse

    exc-jdbi

    Peter329 schrieb:

    Ich frage mich, wie beständig diese Konstanten sind. Können die sich mit der nächsten Version von Windows ändern?
    Da musst Dir keine Sorgen machen das MS die CLSIDs/IIDs der eigenen COM-Interfaces ändert. IShellItem zb gibt es minimum supported client WinXP und das kam 2001 raus und seit dem hat sich die CLSID/IID nicht geändert. Falls MS das mal ändern würde, dann würde auch keine Software mehr funktionieren. Was höchstens passieren kann ist das MS etwas als deprecated einstuft mit dem Hinweis das entsprechendes in zukünftigen Versionen eventuell entfernt wird. Die Betonung liegt hier auf eventuell. Du findest heute noch in Windows Schnittstellen/APIs die seit Ewigkeiten deprecated und noch immer vorhanden sind. Nur aus Gründen der Abwärtskompatibilität. In ganz wenigen Fällen gibt ein asbach alter API Aufruf, der nicht mehr verwendet werden soll, halt entweder nur noch 0 oder <> S_OK zurück oder wird intern, wenn möglich, auf die neue API umgeleitet.

    Peter329 schrieb:

    Und was ist, wenn die Dinger eines Tages nicht mehr funktionieren? Wo bekomme ich denn dann die neuen Werte her?

    Nun, es ist die Aufgabe des Programmierers sich über Änderungen an den Schnittstellen zu informieren und ggf darauf zu reagieren. Wenn Du Dir die MS Doku anschaust, siehst Du entsprechende Informationen wie: In welcher DLL befindet sich der API Aufruf, in welchem HeaderFile finde ich das COM Interface (dort findest du dann auch die IIDs), minimum supported Client/Server, ist der API Aufruf oder eine Funktion eines COM Interfaces deprecated, wenn ja, was soll dann verwendet werden, usw usw. Also Du bekommst schon von MS entsprechende Informationen um Deine Software rechtzeitig anzupassen und wie bereits geschrieben, MS schaltet nicht von heut auf morgen irgendwelche Schnittstellen ab. Wenn dann wäre es eher ein schleichender Prozess der sich über Jahrzehnte hinzieht.

    Nehmen wir mal, weil es hier bestens rein passt, als Beispiel die APIs für die alten OpenFile-, SaveFile- und BrowseForFolder-Dialoge. Diese APIs gab es schon vor WinVista. Seit WinVista (2007 herausgekommen) steht in der MS Doku bei diesen APIs das diese veraltet sind und das man dafür das COM Interface IFileDialog nutzen soll. Ich mein das sind fast 20 Jahre wo es diesen Hinweis gibt und wer es bis heute nicht geschafft hat seine Software entsprechend anzupassen, dem ist nicht mehr zu helfen. Einzige Ausnahme die ich persöhnlich gelten lasse um veraltete Schnittstellen einzusetzen ist: Du bist selbst noch auf einer nicht mehr supporteten Windowsversion unterwegs oder Du entwickelst noch für diese veralteten Windowsversionen. Das gleiche gilt auch für den Verweis auf die Shell und Automation (Shell.BrowseForFolder) <- veraltet, unvollständig usw.
    Mfg -Franky-

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

    Danke für eure Erklärungen. Das klingt dann ja doch wesentlich "harmloser" als es den ersten Anschein hatte.

    Ich habe jetzt angefangen mich mit deinem Beispielprojekt näher zu befassen.

    Da gibt es sechs Start Buttons, deren Funktion mir nicht so recht klar ist ... und die beim Testen irgendwie nicht so richtig zu zünden scheinen:

    Der "OpenFileDialog" sollte doch das ausgewälhle COM-Objekt zurück liefern und sich den ausgewählten Pfad merken. Oder hab ich da irgendetwas falsch verstanden? Oder ist das der "PickFolderDialog" ?

    LG
    Peter
    @Peter329 Die 6 Buttons zeigen die neuen Dialoge in unterschiedlicher Konfiguration (Standard und Customize | mit und ohne Events) und es steht doch genau auf dem Button drauf welcher Dialog gezeigt wird. Was genau meinst Du mit: Zünden nicht?

    Peter329 schrieb:

    Der "OpenFileDialog" sollte doch das ausgewälhle COM-Objekt zurück liefern

    Schau Dir den Code genau an. Das entsprechende COM Interface -> xxx.GetResult gibt auch ein COM Interface zurück (IShellItem). Die Funktion GetResult gibt Dir den Pfad (String) zurück, der vom COM Interface IShellItem ausgelesen wird wobei Du hier noch die Auswahl hast, in welcher Form der Pfad zurück gegeben werden soll. Alle Dialoge merken sich den zuletzt ausgewählten Ordner. Es sei denn Du gibts einen Ordner vor oder Du löscht per ClearClientData die Daten aus der Registry. Im Code sind entsprechende Kommentare hinterlegt was was bewirkt.

    Wichtiger Zusatz: Das Beispiel das Du hier herunterladen kannst, ist ein Testprojekt das Du Deinen Bedürfnissen anpassen musst. Wie im Text auf der Downloadseite vom Beispiel zu lesen ist, habe ich nicht alle COM Interfaces im Beispiel drin. Speziell wenn es um Multiselect geht.
    Mfg -Franky-

    -Franky- schrieb:

    es steht doch genau auf dem Button drauf welcher Dialog gezeigt wird.


    Jau ... den Button Text habe ich schon gelesen ... nur ist dann einem unbedarften Leser nicht unbedingt ganz so klar, was der Button dann auch macht. Ich stecke halt nicht ganz so tief in der Materie drin. :)

    Also ...nach einigem Forschen habe ich dann herausgefunden, dass für meine Zwecke wohl der Button "Pick Folder Dialog" an ehesten geeignet ist. Aber so ganz happy bin ich damit leider nicht.

    Wenn man einen Pfad auswählt, dann wird der in der Registry vermerkt und für den nächsten Aufruf verwendet. Soweit so gut. Nach deinem Hinweis sollte ich das COM-Object in IShellItem zurück erhalten ... obwohl IShellItem als Object definiert ist ... und bisher nicht übergeben wird ... aber das müsste ich halt noch herausfinden. Dieses COM Object könnte ich dann auswerten und damit die Files vom Cell Phone auslesen. Auch das ist in meinem Sinne.

    Allerdings ist die Dialogführung nicht so ganz das was mir gefällt:

    Wenn man den Dialog aufruft und vergessen hat, das Cell Phone zu monitieren, dann kriegt man eine Fehlermeldung (s. Anhang). Diese Fehlermeldung wird sofort vom Auswahlpanel überlagert - die Fehlermeldung sieht man nur kurz aufblitzen und dann isse weg. Nun gut ... das Auswahlpanel kann man verschieben ... aber wenn man dann CANCEL eingibt, ist der gespeicherte Pfad trotzdem futsch! Dann kann ich mein Cell Phone monieren und den Pfad erneut eingeben. Und genau das will ich ja vermeiden. Denn dass Leute das Cell Phone mal nicht montiert haben, kommt schon vor.

    Das Ganze passiert wohl im Aufruf "IFileDialog2.Show" ... und da kann man wohl an der Logik nix ändern.

    Kann man denn den gespeicherten Pfad abprüfen ... und ggfs. wieder restaurieren? "GetResult" liefert ja einen etwas kryptischen String zurück ... könnte man den in My.Settings speichern und dafür verwenden ?

    Das ganze Handling mit dem COM-Objekten ist mir immer noch ziemlich schleierhaft ...

    Also: ein Dialog, den ich mir wünsche, sieht wie folgt aus:

    Der gespeicherte Pfad wird geprüft: ist er nicht vorhanden erhält man eine Fehlermeldung. Nun kann man (ohne Speicherung) mit Cancel aussteigen oder eben mit Continue das Auswahlpanel aufrufen und einen neuen Pfad auswählen. Klingt doch eigentlich ganz vernünftig, oder ?

    LG
    Peter
    Bilder
    • s 2024-02-11 09-51-314.jpg

      7,04 kB, 430×115, 7 mal angesehen

    Peter329 schrieb:

    Der gespeicherte Pfad wird geprüft: ist er nicht vorhanden erhält man eine Fehlermeldung.
    Darüber hatten wir auch schon mal diskutiert. Da .NET nicht mit solchen Pfaden umgehen kann, kannst Du den Pfad mit der API SHCreateItemFromParsingName prüfen. Dann hatten wir auch schon mal über das COM Interface IPortableDeviceManager diskutiert mit dem Du herausfinden kannst, welche WPD Geräte derzeit angeschlossen sind. Schau einfach mal in Deine eigenen Threads zu diesem Thema/Frage.

    Peter329 schrieb:

    Nach deinem Hinweis sollte ich das COM-Object in IShellItem zurück erhalten

    Also das COM Interface IFileDialog2.GetResult gibt ein IShellItem zurück. Die Funktion GetResult in der Klasse FileDialog gibt einen String (Pfad von IShellItem) zurück. Wenn Du nur das COM Interface IShellItem als Rückgabe benötigst, müsstest Du die Funktion in der Klasse entsprechend ändern.

    Peter329 schrieb:

    "GetResult" liefert ja einen etwas kryptischen String zurück

    Jenachdem welchen Wert Du für die Enum SIGDN angibst. Standardmäßig wird sowas wie ::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\\?\usb#vid_04e8&pid_6860&ms_comp_mtp&samsung_android#7&122e7c35&0&0000#{6ac27878-a6fa-4155-ba85-f98f491d4f33}\... zurück gegeben (ThisPC\DeviceID\...).
    Mfg -Franky-

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