Portables Gerät MTP zugreifen mit shell.BrowseForFolder

  • VB.NET

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

    Portables Gerät MTP zugreifen mit shell.BrowseForFolder

    Hallo zusammen

    Für meine Digitalkamera habe ich ein kleines Programm, welches mir die neuen Bilder von der Kamera auf den PC kopiert. Dabei werden die Rohdateibilder in einen anderen Ordner kopiert als die JPG Dateien. Die guten Bilder markiere ich und kann diese dann in einen anderen Ordner verschieben, auch die Rohdateibilder.

    Nun habe ich aber eine Kamera für meine Tochter gekauft, welche als Portables Gerät angezeigt wird. Ich habe nun etwas geforscht, bin z.B. auf diesen Artikel gelangt: Cell Phone als USB device

    Nun habe ich mit Shell.BrowseForFolder relativ einfach gegen den Rat von Franky Zugriff auf die Kamera. Der Grund ist, ich habe überhaupt keine Grundlagen für Zugriffe auf API.

    Mein Problem. Wie kann ich den ausgewählten Pfad speichern, dass ich nicht jedes mal den Browser starten muss? Mit der Funktion Ordnerpfad_ermittlen() kann ich den Pfad auslesen, mit diesem Pfad kann ich aber kein Folder erzeugen. Mit den Normalen Pfade (z.B. "C:\Temp\) kann ich problemlos ein Folder erstellen.

    Besten Dank für Eure Tipps





    Aufruf:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Sub BT_ordnerpfad_Click(sender As Object, e As EventArgs) Handles BT_ordnerpfad.Click
    2. TB_ordnerpfad.Text = MTP_Kopieren.Ordnerpfad_ermittlen()
    3. End Sub
    4. Private Sub BT_Kopieren_Click(sender As Object, e As EventArgs) Handles BT_Kopieren.Click
    5. Call MTP_Kopieren.Dateien_Kopieren(TB_ordnerpfad.Text, "c:\temp\mi8", "c:\temp\mi8\arw", ".arw", "c:\temp\guteBilder")
    6. End Sub



    Modul:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. 'https://www.vb-paradise.de/index.php/Thread/133529-Cell-Phone-als-USB-device/
    2. Imports System.IO
    3. Imports Shell32
    4. Module MTP_Kopieren
    5. Public Function Ordnerpfad_ermittlen() As String
    6. 'für Import von Kamera oder auch Natel:
    7. ' + Reference to the COM Component "Microsoft Shell Controls and Automation"
    8. ' + Include Shell32 Namespace
    9. '"Microsoft Shell Controls and Automation" causes error BC31541 (Interop type cannot be added).
    10. 'To avoid this error, click this reference and set:
    11. ' Accept Interop Types=False
    12. Dim shell = New Shell()
    13. Dim Hwnd As Integer
    14. Dim FromFolder = shell.BrowseForFolder(Hwnd, "Choose folder", 0, 17)
    15. If FromFolder Is Nothing Then
    16. MsgBox("*** OK, cancelled.")
    17. Return ""
    18. End If
    19. 'This is how to get the real path form the folder object, which is a cryptic expression:
    20. 'Dim fi As FolderItem = (TryCast(FromFolder, Folder3)).Self
    21. 'Dim path = fi.Path 'Just for your info
    22. 'Note: FromFolder is a COM-Object, so it cannot be used like a .NET directory
    23. 'Copy all files from cell phone to PC directory
    24. Dim folder = DirectCast(FromFolder, Folder3)
    25. Ordnerpfad_ermittlen = folder.Self.Path.ToString
    26. End Function
    27. Sub Dateien_Kopieren(Quellordner As String, Zielordner As String, ZielordnerRAW As String, Rohdateiendung As String, GutebilderOrdner As String)
    28. Dim shell = New Shell()
    29. Dim Hwnd As Integer
    30. 'Dim quellordnerShell As Folder3 = DirectCast(shell.NameSpace("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\\?\usb#vid_2717&pid_ff40#3803ec03#{6ac27878-a6fa-4155-ba85-f98f491d4f33}\SID-{10001,,56481779712}\{E903B08B-FFFF-FFFF-0000-000000000000}\{54CDAA72-0000-0000-0000-000000000000}"), Folder3)
    31. Dim quellordnerShell As Folder3 = DirectCast(shell.NameSpace(Quellordner), Folder3)
    32. Dim ZielordnerShell As Folder3 = DirectCast(shell.NameSpace(Zielordner), Folder3)
    33. Dim ZielordnerRAWshell As Folder3 = DirectCast(shell.NameSpace(ZielordnerRAW), Folder3)
    34. If quellordnerShell Is Nothing Then
    35. quellordnerShell = shell.BrowseForFolder(Hwnd, "Choose folder", 0, 17)
    36. End If
    37. 'Dim folder = DirectCast(srcFolder, Folder3)
    38. For Each item In quellordnerShell.Items
    39. If TypeOf item Is Folder3 Then
    40. Dim subFolder = DirectCast(item, Folder3)
    41. ElseIf TypeOf item Is FolderItem2 Then
    42. Dim fileItem = DirectCast(item, FolderItem2)
    43. If File.Exists(IO.Path.Combine(Zielordner, fileItem.Name.ToString)) Or
    44. File.Exists(IO.Path.Combine(ZielordnerRAW, fileItem.Name.ToString)) Or
    45. File.Exists(IO.Path.Combine(GutebilderOrdner, fileItem.Name.ToString)) Or
    46. File.Exists(IO.Path.Combine(GutebilderOrdner, "raw", fileItem.Name.ToString)) Then
    47. Else
    48. If fileItem.Name.Substring(fileItem.Name.Length - Rohdateiendung.Length).ToLower = Rohdateiendung.ToString.ToLower Then
    49. ZielordnerRAWshell.CopyHere(fileItem)
    50. Debug.Print("Rohdatei: " & fileItem.Name.ToString)
    51. ' Kopieren der Datei
    52. Else
    53. 'für z.B. Filme
    54. ZielordnerShell.CopyHere(fileItem)
    55. Debug.Print("JPG: " & fileItem.Name.ToString)
    56. End If
    57. End If
    58. End If
    59. Next
    60. End Sub
    61. End Module

    ErfinderDesRades schrieb:

    warum nicht? was ist besonderes an dem Pfad, den Ordnerpfad_ermittlen() returnt?
    Vermutlich weil der zurück gegebene Pfad so aussieht: "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\\?\usb#vid_2717&pid_ff40#3803ec03#{6ac27878-a6fa-4155-ba85-f98f491d4f33}\SID-{10001,,56481779712}\{E903B08B-FFFF-FFFF-0000-000000000000}\{54CDAA72-0000-0000-0000-000000000000}"

    @Panter Ich kenne mich mit dem Verweis auf die Shell nicht aus. Ich bin ja eher für direkte Nutzung der COM-Interfaces ohne Verweise.Wie auch immer, es müsste sowas wie GetDisplayName geben wo du über eine Enum SIGDN -> learn.microsoft.com/en-us/wind…re/ne-shobjidl_core-sigdn auswählen kannst, wie der zurück gegebene Pfad formatiert werden soll. Unter anderem auch so, wie Du Ihn auch im Explorer sehen kannst. Schaut dann ungefähr so aus "Dieser PC (::{20D04FE0-3AEA-1069-A2D8-08002B30309D})\DeviceID (\\?\usb#vid_2717&pid_ff40#3803ec03#{6ac27878-a6fa-4155-ba85-f98f491d4f33})\Interner Speicher (SID-{10001,,56481779712})\DCIM ({E903B08B-FFFF-FFFF-0000-000000000000})\usw. Allerdings ohne diese CLSIDs/StorageID usw.
    Mfg -Franky-

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

    Hallo ErfinderdesRades, hallo Franky

    Besten Dank für eure Antworten. Ja, der Pfad ist ungefär so: "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\\?\usb#vid_2717&pid_ff40#3803ec03#{6ac27878-a6fa-4155-ba85-f98f491d4f33}\SID-{10001,,56481779712}\{E903B08B-FFFF-FFFF-0000-000000000000}\{54CDAA72-0000-0000-0000-000000000000}"

    Ich werde wohl mich etwas in die Nutzung der COM-Interfaces einlesen müssen. Ich habe noch ein Projekt gefunden, welches ich an meine Bedürfnisse anpassen kann. Falls Ihr mir einen Link habt, wo die Grundlagen etwas einfach erklärt würden, wäre ich sehr dankbar.

    LG Panter
    Hi, evtl. hilft Dir das zum Verständnis auch weiter: FolderBrowserDialog in .Net nachbilden Da kannst Du mal mit der Enum SIGDN spielen und schauen was da wie als Pfad zurück gegeben wird. Das müsste doch auch mit dem Verweis auf die Shell funktionieren.

    Edit: Das ist ein ganz altes Bsp. von mir das nur mit den WPD COM-Interfaces funktioniert. Da das nur ein Testprojekt ist, ist der Code auch grottig. ;) activevb.de/cgi-bin/upload/search.pl und dort nach VBC_VBN_WindowsPortableDevice.zip suchen. Die beiden VB6 Projekte kannst ja löschen.
    Mfg -Franky-

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

    Hallo Franky

    Dein angeblich grottig programmiertes Projekt auf ActiveVB.de ist genau das was ich gesucht habe. Ich habe eine Form2 erstellt, und einfach dein Code auf meine Bedürfnisse angepasst. Ich habe zwar keine Ahnung wie das Ganze funktioniert - Aber meine ersten Versuche waren von Erfolg gekrönt. Falls jemand auch so etwas Programmieren möchte - ich habe mal meinen Code angefügt. Dabei greife ich immer auf den selbenOrdner - DCIM - Diverses - welcher zuerst gesucht wird. Beim Testen war die ObjID von diesem Ordner auf einmal eine andere. Mit GetContentFolder bzw. etContentFiles kann man ja die Daten auslesen und mit mnuCopyFrom kopieren.

    Einfach toll - Besten Dank

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Option Strict On
    2. Option Explicit On
    3. Imports System.IO
    4. Imports System.ComponentModel
    5. Public Class Form2
    6. Private Structure Portable_Dateiliste
    7. Dim Dateinamen As String
    8. Dim sObjID As String
    9. End Structure
    10. Private Structure Portable_Ordner
    11. Dim OrdnerName As String
    12. Dim sObjID As String
    13. End Structure
    14. Private List_Dateien As New List(Of Portable_Dateiliste)
    15. Private List_Ordner As New List(Of portable_Ordner)
    16. Private sDevID As String = String.Empty
    17. Private sPnpDevIDs As String() = New String() {}
    18. Private currIPDev As PortableDevice
    19. Private currIPDevValues As PortableDeviceValues
    20. Private currIPDevContent As PortableDeviceContent
    21. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    22. Dim ObjID As String = ""
    23. sDevID = "\\?\usb#vid_2717&pid_ff40#3803ec03#{6ac27878-a6fa-4155-ba85-f98f491d4f33}"
    24. 'Verbindung herstellen und List OF (List_Dateien füllen)
    25. currIPDevValues = New PortableDeviceValues
    26. currIPDevValues.SetStringValue(GetPropertyKey(PropertyKeys.WPD_CLIENT_NAME), "WPD-Demo")
    27. currIPDevValues.SetStringValue(GetPropertyKey(PropertyKeys.WPD_CLIENT_MAJOR_VERSION), "1")
    28. currIPDevValues.SetStringValue(GetPropertyKey(PropertyKeys.WPD_CLIENT_MINOR_VERSION), "0")
    29. currIPDevValues.SetStringValue(GetPropertyKey(PropertyKeys.WPD_CLIENT_REVISION), "0")
    30. currIPDev = New PortableDevice
    31. If currIPDev.Open(sDevID, currIPDevValues) = True Then
    32. currIPDevContent = currIPDev.Content
    33. 'Quellordner suchen - Device - DCIM - Diverses
    34. EnumerateContent(currIPDevContent, "DEVICE", 0, True, True) '
    35. EnumerateContent(currIPDevContent, List_Dateien(0).sObjID.ToString, 0, True, True)
    36. ObjID = List_Ordner.FirstOrDefault(Function(folder) folder.OrdnerName = "DCIM").sObjID
    37. EnumerateContent(currIPDevContent, ObjID, 0, True, True)
    38. ObjID = List_Ordner.FirstOrDefault(Function(folder) folder.OrdnerName = "Diverses").sObjID
    39. If ObjID Is Nothing Then
    40. MsgBox("Fehler - Ordner wurde nicht gefunden")
    41. currIPDev.Close()
    42. Return
    43. End If
    44. GetContentFiles(currIPDevContent, ObjID)
    45. Call WDP_Kopieren(currIPDevContent, "c:\temp\mi8", ".arw", "c:\temp\guteBilder")
    46. MsgBox("fertig")
    47. currIPDev.Close()
    48. 'GetSupportedProperties(currIPDevContent, WPD_DEVICE_OBJECT_ID, lbProperties)
    49. 'GetSupportedCommands(currIPDev)
    50. 'ResetDevice(currIPDev)
    51. 'ContentLocation(currIPDev)
    52. 'StorageEject(currIPDev)
    53. 'StorageFormat(currIPDev)
    54. 'SmsSend(currIPDev)
    55. 'StillImageCapture(currIPDev)
    56. 'ListDeviceStorage(currIPDevContent)
    57. End If
    58. End Sub
    59. Sub WDP_Kopieren(Content As PortableDeviceContent, Zielordner As String, Rohdateiendung As String, GutebilderOrdner As String)
    60. Dim Zielordner_raw As String = Path.Combine(Zielordner, "arw")
    61. Dim GuteBilder_Raw As String = Path.Combine(GutebilderOrdner, "arw")
    62. For Each x In List_Dateien
    63. If File.Exists(IO.Path.Combine(Zielordner, x.Dateinamen.ToString)) Or
    64. File.Exists(IO.Path.Combine(Zielordner_raw, x.Dateinamen.ToString)) Or
    65. File.Exists(IO.Path.Combine(GutebilderOrdner, x.Dateinamen.ToString)) Or
    66. File.Exists(IO.Path.Combine(GuteBilder_Raw, x.Dateinamen.ToString)) Then
    67. Debug.Print("Datei vorhanden: " & x.Dateinamen.ToString)
    68. Else
    69. If x.Dateinamen.ToString.Substring(x.Dateinamen.ToString.Length - Rohdateiendung.Length).ToLower = Rohdateiendung.ToString.ToLower Then
    70. mnuCopyFrom(Content, x.sObjID, Zielordner_raw)
    71. Debug.Print("Kopiere Rohdatei: " & x.Dateinamen.ToString)
    72. Else
    73. 'für z.B. Filme / JPG
    74. mnuCopyFrom(Content, x.sObjID, Zielordner)
    75. Debug.Print("Kopiere JPG: " & x.Dateinamen.ToString)
    76. End If
    77. End If
    78. Next
    79. End Sub
    80. Private Sub GetContentFiles(ByVal Content As PortableDeviceContent, ByVal ParentID As String)
    81. 'lbFile.Items.Clear()
    82. EnumerateContent(Content, ParentID, 0, False, False)
    83. End Sub
    84. Private Sub GetContentFolder(ByVal Content As PortableDeviceContent, ByVal ParentID As String, ByVal ParentName As String)
    85. EnumerateContent(Content, ParentID, 1, True, True)
    86. End Sub
    87. Private Sub EnumerateContent(ByVal Content As PortableDeviceContent, ByVal ParentID As String,
    88. Optional ByVal Deep As Integer = 0, Optional ByVal FolderOnly As Boolean = False,
    89. Optional ByVal RecursiveFolder As Boolean = False)
    90. List_Dateien.Clear()
    91. List_Ordner.Clear()
    92. Using Properties As PortableDeviceProperties = Content.Properties
    93. Using Value As New PortableDeviceValues
    94. Using ObjectIDs As EnumPortableDeviceObjectIDs = Content.EnumObjects(0, ParentID, Value)
    95. Dim sObjID As String() = New String() {}
    96. Dim uFetched As UInteger
    97. If ObjectIDs.Next(1, sObjID, uFetched) = True Then
    98. Do While uFetched <> 0
    99. Application.DoEvents()
    100. Dim currentObject As WpdStruct.PDF = WrapObject(Properties, sObjID(0))
    101. If currentObject.PDFF_Type = WPD_CONTENT_TYPE_FOLDER Then
    102. Dim Eintrag As New Portable_Ordner
    103. Eintrag.OrdnerName = currentObject.PDFF_OriginalName
    104. Eintrag.sObjID = currentObject.PDFF_ObjectId
    105. Debug.Print(Eintrag.OrdnerName.ToString & " " & Eintrag.sObjID.ToString)
    106. List_Ordner.Add(Eintrag)
    107. Else
    108. Dim Eintrag As New Portable_Dateiliste
    109. Eintrag.Dateinamen = currentObject.PDFF_OriginalName
    110. Eintrag.sObjID = currentObject.PDFF_ObjectId
    111. Debug.Print(Eintrag.Dateinamen.ToString & " " & Eintrag.sObjID.ToString)
    112. List_Dateien.Add(Eintrag)
    113. End If
    114. ObjectIDs.Next(1, sObjID, uFetched)
    115. Loop
    116. End If
    117. End Using
    118. End Using
    119. End Using
    120. End Sub
    121. Private Function WrapObject(ByVal Properties As PortableDeviceProperties, ByVal objectId As String) As WpdStruct.PDF
    122. Dim tRet As New WpdStruct.PDF
    123. Using Keys = New PortableDeviceKeyCollection
    124. Call Keys.Add(GetPropertyKey(PropertyKeys.WPD_OBJECT_ID))
    125. Call Keys.Add(GetPropertyKey(PropertyKeys.WPD_OBJECT_PARENT_ID))
    126. Call Keys.Add(GetPropertyKey(PropertyKeys.WPD_OBJECT_NAME))
    127. Call Keys.Add(GetPropertyKey(PropertyKeys.WPD_OBJECT_ORIGINAL_FILE_NAME))
    128. Call Keys.Add(GetPropertyKey(PropertyKeys.WPD_OBJECT_CONTENT_TYPE))
    129. Using Values = Properties.GetValues(objectId, Keys)
    130. If Values.GetCount > 0 Then
    131. tRet.PDFF_ObjectId = Values.GetStringValue(GetPropertyKey(PropertyKeys.WPD_OBJECT_ID))
    132. tRet.PDFF_ParentId = Values.GetStringValue(GetPropertyKey(PropertyKeys.WPD_OBJECT_PARENT_ID))
    133. tRet.PDFF_Name = Values.GetStringValue(GetPropertyKey(PropertyKeys.WPD_OBJECT_NAME))
    134. tRet.PDFF_OriginalName = Values.GetStringValue(GetPropertyKey(PropertyKeys.WPD_OBJECT_ORIGINAL_FILE_NAME))
    135. tRet.PDFF_Type = Values.GetStringValue(GetPropertyKey(PropertyKeys.WPD_OBJECT_CONTENT_TYPE))
    136. End If
    137. End Using
    138. End Using
    139. Return tRet
    140. End Function
    141. Private Sub mnuCopyFrom(ByVal Content As PortableDeviceContent, ByVal ObjId As String, ZielOrdner As String)
    142. Using IPDevResources As PortableDeviceResources = Content.Transfer
    143. Dim optimalTransferSize As UInteger
    144. Using IContentStream As Stream = IPDevResources.GetStream(ObjId, GetPropertyKey(PropertyKeys.WPD_RESOURCE_DEFAULT),
    145. WpdStruct.STGM.STGM_READ, optimalTransferSize)
    146. Dim fileSize = GetPropertyInt(Content, ObjId, GetPropertyKey(PropertyKeys.WPD_OBJECT_SIZE))
    147. Dim fileName = GetPropertyStr(Content, ObjId, GetPropertyKey(PropertyKeys.WPD_OBJECT_ORIGINAL_FILE_NAME))
    148. 'Using fsd As New SaveFileDialog
    149. 'fsd.Title = "Save File"
    150. 'fsd.FileName = fileName
    151. 'fsd.ShowDialog()
    152. 'If fsd.FileName <> "" Then
    153. If fileSize > 0 Then
    154. Dim copyfileBuffer As Boolean = False
    155. Dim fileBuffer As Byte() = New Byte(fileSize - 1) {}
    156. If fileSize <= optimalTransferSize Then
    157. If IContentStream.Read(fileBuffer, CUInt(fileSize)) = fileSize Then
    158. copyfileBuffer = True
    159. End If
    160. Else
    161. Dim copyItem As UInteger
    162. Dim copyCount As UInteger = CUInt(fileSize \ optimalTransferSize)
    163. Dim lastBytes As UInteger = CUInt(fileSize - (copyCount * optimalTransferSize))
    164. Dim tmpfileBuffer As Byte() = New Byte(CInt(optimalTransferSize - 1)) {}
    165. For copyItem = 0 To CUInt(copyCount - 1)
    166. If IContentStream.Read(tmpfileBuffer, optimalTransferSize) = optimalTransferSize Then
    167. copyfileBuffer = True
    168. Array.Copy(tmpfileBuffer, 0, fileBuffer, optimalTransferSize * copyItem, optimalTransferSize)
    169. Else
    170. copyfileBuffer = False
    171. End If
    172. Next copyItem
    173. Array.Resize(tmpfileBuffer, CInt(lastBytes))
    174. If IContentStream.Read(tmpfileBuffer, lastBytes) = lastBytes Then
    175. copyfileBuffer = True
    176. Array.Copy(tmpfileBuffer, 0, fileBuffer, optimalTransferSize * copyItem, lastBytes)
    177. Else
    178. copyfileBuffer = False
    179. End If
    180. tmpfileBuffer = Nothing
    181. End If
    182. If copyfileBuffer = True Then
    183. Using fs As FileStream = New FileStream(Path.Combine(ZielOrdner, fileName), FileMode.Create) 'Type(fsd.OpenFile(), System.IO.FileStream)
    184. fs.Write(fileBuffer, 0, fileBuffer.Length)
    185. fs.Close()
    186. End Using
    187. fileBuffer = Nothing
    188. 'MsgBox("File under '" & fsd.FileName & "' saved.", MsgBoxStyle.Information Or MsgBoxStyle.OkOnly, "Save File")
    189. End If
    190. End If
    191. 'End If
    192. 'End Using
    193. End Using
    194. End Using
    195. End Sub
    196. End Class

    Panter schrieb:

    Dein angeblich grottig programmiertes Projekt

    Doch doch, ist grottig. Das wird Dir jeder hier bestätigen können. Aber darum geht es auch nicht in einem Testprojekt. Das zeigt nur wie es funktioniert. Die WPD COM-Interfaces sind schon Recht speziell und da geht noch viel mehr als das, was ich da im Testprojekt zeige. Falls Du Dich da einarbeiten möchtest: learn.microsoft.com/de-de/wind…/windows-portable-devices und weitere Funde in der MS-Doku bzw. im Internet zu diesem Thema. Evtl. bastel ich irgendwann mal noch ein Bsp. per WinRT (UWP): learn.microsoft.com/de-de/uwp/…portable?view=winrt-22621 für VB6 und VB.NET.
    Mfg -Franky-