Auslesen einer Verknüpfung mit WSHSHELL - HRESULT: 0x80020009

  • VB.NET

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

    Auslesen einer Verknüpfung mit WSHSHELL - HRESULT: 0x80020009

    Nachdem das Auslesen der Verknüpfungen mit der interop.shell32 Methode auf Rechnern ohne Adminrechten gescheitert ist, habe ich die WSH-Methode gewählt.

    VB.NET-Quellcode

    1. Public Function LeseShellLinkPath(ByVal sLink As String) As String
    2. ' der mitgelieferte Link ist: sLink
    3. Dim Shortcut As IWshRuntimeLibrary.IWshShortcut = wshWSHSHELL.CreateShortcut(sLink)
    4. Dim sPfad As String = Shortcut.TargetPath
    5. Dim sLinkArguments As String = Shortcut.Arguments
    6. ' Rückgabewert der Funktion
    7. Return sPfad & " " & sLinkArguments
    8. End Function


    Auf meinen Windows 7 Clients funktioniert das jetzt wunderbar - egal ob mit oder ohne Admin.
    Sorgen machen mir die XP-Clients, die ich leider noch mitschleppen muss.
    Dort kommt ohne Admin folgender Fehler:

    System.Runtime.InteropServices.COMException (0x80020009): Ausnahmefehler aufgetreten. (Ausnahme von HRESULT: 0x80020009 (DISP_E_EXCEPTION))
    Spoiler anzeigen
    ************** Ausnahmetext **************

    bei IWshRuntimeLibrary.IWshShell3.CreateShortcut(String PathLink)
    bei MeinInfoCenterV3.Form1.LeseShellLinkPath(String sLink)
    bei MeinInfoCenterV3.Form1.GetListOfLinksOnDesktop()
    bei MeinInfoCenterV3.Form1.Form1_Load(Object sender, EventArgs e)
    bei System.EventHandler.Invoke(Object sender, EventArgs e)
    bei System.Windows.Forms.Form.OnLoad(EventArgs e)
    bei System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
    bei System.Windows.Forms.Control.CreateControl()
    bei System.Windows.Forms.Control.WmShowWindow(Message& m)
    bei System.Windows.Forms.Control.WndProc(Message& m)
    bei System.Windows.Forms.Form.WndProc(Message& m)
    bei System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)


    So langsam verzweifel ich...

    Gibt es denn keine .Net Methoden um Verknüpfungen auszulesen?
    Cool wäre auch eine .Net Methode um den Papierkorb auszulesen und zu leeren. Die Methoden, die ich dafür gefunden und angewendet habe, basierten auch immer auf einer COM-Komponente, die als Verweis hinzugefügt werden muss. Und genau diese Dinge funzen bei mir nicht überall...

    Hier soll die App via ClickOnce bereitgestellt werden. Das schränkt schon etwas ein.
    Any help?
    so far...
    Wumpel


    ...manchmal ist es einfacher als man denkt...
    Frag mal Frau Google nach 0x80020009.
    Möglicherweise ist eine COM-Komponente nicht registriert.
    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!
    Google ist doch immer meine erste Anlaufstelle ;)
    Auch nach deiner Antwort habe ich intensiv mit Frau google gearbeitet.
    Leider finde ich dort keine Hinweise, DAS oder WIE ich die entsprechende Komponente registrieren soll.
    Einzig ein Beitrag hat sich mit einer OCX befasst (WSHom.ocx), welche ich dann manuell registriert habe. Das Ergebnis ist allerdings das selbe.
    Beim Aufruf erscheint die o.g. Fehlermeldung.

    Wie ist das denn generell? Wenn ich einen Verweis erstelle, dann wird eine interop.blabla erstellt, die als wrapper zwischen meinem Programm und der original DLL / OCX fungiert, oder?
    Mit VS auf meinem Win7 Rechner erstelltes Programm läuft auf keinem XP Rechner (0x80020009).

    Bitte keine Hinweise mehr auf google... Was muss ich tun, damit der o.g. Fehler nicht mehr auftaucht?
    so far...
    Wumpel


    ...manchmal ist es einfacher als man denkt...

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

    Nun - angesichts deines anderen Threads zum Shell32-Interop, der ja auf XP, aber nicht auf Win7 funktioniert, wäre die naheliegendste Lösung wohl folgende: Teste zuerst, auf welchem System dein Programm läuft. Dann benutzt du Shell32 auf XP und WSH auf Windows 7. Quick&Dirty eben. Ich schau mal, ob ich eine Lösung finde, die für beide Systeme funktioniert.

    Edit: Ich habe mich mal damit beschäftigt und die Shell32-Variante auf Win7 zum Laufen gebracht. Sie funktioniert bei mir ohne Administratorrechte. Auf WinXP habe ich das Beispiel (Konsolenanwendung) nicht getestet. Wichtig: IShellDispatch, Folder, FolderItems und FolderItem müssen manuell deklariert werden, im Interface FolderItem sind kleinere Anpassungen nötig. Die Interfaces können gekürzt werden.

    Deklarationen:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. <ComImport(), Guid("D8F015C0-C278-11CE-A49E-444553540000")> _
    2. Public Interface IShellDispatch
    3. ReadOnly Property Application As <MarshalAs(UnmanagedType.IDispatch)> Object
    4. ReadOnly Property Parent As <MarshalAs(UnmanagedType.IDispatch)> Object
    5. Function [NameSpace](<[In](), MarshalAs(UnmanagedType.Struct)> ByVal vDir As Object) As <MarshalAs(UnmanagedType.Interface)> Folder
    6. 'Alles ab hier kann weggelassen werden.
    7. Function BrowseForFolder(<[In]()> ByVal Hwnd As Integer, <[In](), MarshalAs(UnmanagedType.BStr)> ByVal Title As String, <[In]()> ByVal Options As Integer, <[In](), MarshalAs(UnmanagedType.Struct)> Optional ByVal RootFolder As Object = Nothing) As <MarshalAs(UnmanagedType.Interface)> Folder
    8. Function Windows() As <MarshalAs(UnmanagedType.IDispatch)> Object
    9. Sub Open(<[In](), MarshalAs(UnmanagedType.Struct)> ByVal vDir As Object)
    10. Sub Explore(<[In](), MarshalAs(UnmanagedType.Struct)> ByVal vDir As Object)
    11. Sub MinimizeAll()
    12. Sub UndoMinimizeALL()
    13. Sub FileRun()
    14. Sub CascadeWindows()
    15. Sub TileVertically()
    16. Sub TileHorizontally()
    17. Sub ShutdownWindows()
    18. Sub Suspend()
    19. Sub EjectPC()
    20. Sub SetTime()
    21. Sub TrayProperties()
    22. Sub Help()
    23. Sub FindFiles()
    24. Sub FindComputer()
    25. Sub RefreshMenu()
    26. Sub ControlPanelItem(<[In](), MarshalAs(UnmanagedType.BStr)> ByVal bstrDir As String)
    27. End Interface
    28. <ComImport(), DefaultMember("Title"), Guid("BBCBDE60-C3FF-11CE-8350-444553540000")> _
    29. Public Interface Folder
    30. ReadOnly Property Title As <MarshalAs(UnmanagedType.BStr)> String
    31. ReadOnly Property Application As <MarshalAs(UnmanagedType.IDispatch)> Object
    32. ReadOnly Property Parent As <MarshalAs(UnmanagedType.IDispatch)> Object
    33. ReadOnly Property ParentFolder As <MarshalAs(UnmanagedType.Interface)> Folder
    34. Function Items() As <MarshalAs(UnmanagedType.Interface)> FolderItems
    35. 'snip
    36. Function ParseName(<[In](), MarshalAs(UnmanagedType.BStr)> ByVal bName As String) As <MarshalAs(UnmanagedType.Interface)> FolderItem
    37. Sub NewFolder(<[In](), MarshalAs(UnmanagedType.BStr)> ByVal bName As String, <[In](), MarshalAs(UnmanagedType.Struct)> Optional ByVal vOptions As Object = Nothing)
    38. Sub MoveHere(<[In](), MarshalAs(UnmanagedType.Struct)> ByVal vItem As Object, <[In](), MarshalAs(UnmanagedType.Struct)> Optional ByVal vOptions As Object = Nothing)
    39. Sub CopyHere(<[In](), MarshalAs(UnmanagedType.Struct)> ByVal vItem As Object, <[In](), MarshalAs(UnmanagedType.Struct)> Optional ByVal vOptions As Object = Nothing)
    40. Function GetDetailsOf(<[In](), MarshalAs(UnmanagedType.Struct)> ByVal vItem As Object, <[In]()> ByVal iColumn As Integer) As <MarshalAs(UnmanagedType.BStr)> String
    41. End Interface
    42. <ComImport(), Guid("744129E0-CBE5-11CE-8350-444553540000")> _
    43. Public Interface FolderItems
    44. Inherits IEnumerable
    45. ReadOnly Property Count As Integer
    46. ReadOnly Property Application As <MarshalAs(UnmanagedType.IDispatch)> Object
    47. ReadOnly Property Parent As <MarshalAs(UnmanagedType.IDispatch)> Object
    48. Function Item(<[In](), MarshalAs(UnmanagedType.Struct)> Optional ByVal index As Object = Nothing) As <MarshalAs(UnmanagedType.Interface)> FolderItem
    49. 'snip
    50. Overloads Function GetEnumerator() As <MarshalAs(UnmanagedType.CustomMarshaler, MarshalType:="", MarshalTypeRef:=GetType(CustomMarshalers.EnumeratorToEnumVariantMarshaler), MarshalCookie:="")> IEnumerator
    51. End Interface
    52. <ComImport(), Guid("FAC32C80-CBE4-11CE-8350-444553540000")> _
    53. Public Interface FolderItem
    54. ReadOnly Property Application As <MarshalAs(UnmanagedType.IDispatch)> Object
    55. ReadOnly Property Parent As <MarshalAs(UnmanagedType.IDispatch)> Object
    56. Property Name As <MarshalAs(UnmanagedType.BStr)> String
    57. ReadOnly Property Path As <MarshalAs(UnmanagedType.BStr)> String
    58. <PreserveSig()> _
    59. Function GetLink(<Out(), MarshalAs(UnmanagedType.IDispatch)> ByRef pLink As Object) As Int32
    60. 'ReadOnly Property GetLink As <MarshalAs(UnmanagedType.IDispatch)> Object
    61. ReadOnly Property GetFolder As <MarshalAs(UnmanagedType.IDispatch)> Object
    62. ReadOnly Property IsLink As Boolean
    63. 'snip
    64. ReadOnly Property IsFolder As Boolean
    65. ReadOnly Property IsFileSystem As Boolean
    66. ReadOnly Property IsBrowsable As Boolean
    67. Property ModifyDate As DateTime
    68. ReadOnly Property Size As Integer
    69. ReadOnly Property Type As <MarshalAs(UnmanagedType.BStr)> String
    70. Function Verbs() As <MarshalAs(UnmanagedType.Interface)> Object 'FolderItemVerbs
    71. Sub InvokeVerb(<[In](), MarshalAs(UnmanagedType.Struct)> Optional ByVal vVerb As Object = Nothing)
    72. End Interface

    Code:

    VB.NET-Quellcode

    1. Public Shared Sub Main(ByVal argv() As String)
    2. Dim IID_IShellDispatch As New Guid("D8F015C0-C278-11CE-A49E-444553540000")
    3. Dim IID_ShellLinkObject As New Guid("317EE249-F12E-11D2-B1E4-00C04F8EEB3E")
    4. Dim CLSID_Shell As New Guid("13709620-C279-11CE-A49E-444553540000")
    5. Dim pShell As IntPtr = IntPtr.Zero
    6. Dim hr As Int32 = NativeMethods.CoCreateInstance(CLSID_Shell, Nothing, 1, IID_IShellDispatch, pShell)
    7. If hr <> 0 Then
    8. Console.WriteLine("Could not create Shell Object.")
    9. Console.WriteLine("ERROR: " & Marshal.GetExceptionForHR(hr).Message)
    10. End If
    11. Dim oShell As Object = Marshal.GetObjectForIUnknown(pShell)
    12. Dim shellDispatch As IShellDispatch = CType(oShell, IShellDispatch)
    13. Dim f As Folder = shellDispatch.NameSpace(Environment.GetFolderPath(Environment.SpecialFolder.Desktop))
    14. Dim fItems As FolderItems = f.Items()
    15. Dim fi As FolderItem
    16. For i = 0 To fItems.Count - 1
    17. fi = fItems.Item(i)
    18. If fi.IsLink Then
    19. wl("Link found: " & fi.Name)
    20. Dim tmpLink As Object = Nothing
    21. hr = fi.GetLink(tmpLink)
    22. If hr <> 0 Then
    23. Console.WriteLine("GetLink ERROR: " & Marshal.GetExceptionForHR(hr).Message)
    24. Continue For
    25. End If
    26. Dim pUnkLink As IntPtr = Marshal.GetIUnknownForObject(tmpLink)
    27. Marshal.ReleaseComObject(tmpLink)
    28. Dim ppv As IntPtr
    29. hr = Marshal.QueryInterface(pUnkLink, IID_ShellLinkObject, ppv)
    30. Marshal.Release(pUnkLink)
    31. Dim oLink As Object = Marshal.GetObjectForIUnknown(ppv)
    32. Dim link As Shell32.ShellLinkObject = CType(oLink, Shell32.ShellLinkObject)
    33. Marshal.Release(ppv)
    34. Console.WriteLine(" --> " & link.Path)
    35. Marshal.ReleaseComObject(link)
    36. Else
    37. Console.WriteLine("Object found: " & fi.Name)
    38. End If
    39. Marshal.ReleaseComObject(fi)
    40. Next
    41. Marshal.ReleaseComObject(fItems)
    42. Marshal.ReleaseComObject(f)
    43. Marshal.ReleaseComObject(shellDispatch)
    44. Console.ReadLine()
    45. End Sub
    Gruß
    hal2000

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

    @hal2000

    VIELEN LIEBEN DANK für deine Mühe.
    Ich werde Montag sofort deinen Vorschlag einarbeiten und testen.
    Wenn das klappt bist du mein persönlicher Held ;)

    EDIT:
    Boah - ich schreibe das nicht gerne: aber ich blicke da jetzt nicht dran lang. Das ist mir zu hoch. Da komme ich als Hobbyist nicht weiter :(
    Ich werde mir das jetzt mühsam erarbeiten, weil ich verstehen will, was in meinem Code enthalten ist - aber das wird vermutlich ne ganze Weile dauern.

    Da ich derzeit überhaupt nicht verstehe was da abgehen soll und ich beim Einfügen diverse Fehler habe, werde ich das erst einmal bei Seite legen und die Variante versuchen "Wenn XP, dann Shell32..."
    so far...
    Wumpel


    ...manchmal ist es einfacher als man denkt...

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

    Warum bin ich da nicht selbst drauf gekommen?
    Tse tse tse.
    Manchmal sieht man den Wald vor lauter Bäumen nicht.
    Betriebssystemabfrage - die passende Methode dazu - fertig.
    Vielen Dank an alle, die mitgewirkt haben!
    so far...
    Wumpel


    ...manchmal ist es einfacher als man denkt...

    Wumpel schrieb:

    Das ist mir zu hoch.
    Das kann ich verstehen - den Code könnte man durchaus auch als Harcore-Interop bezeichnen. Prinzipiell ist das nichts anderes als die teilweise Nachbildung des Codes, wie man ihn in C dafür schreiben würde. Wenn du mit COM nicht vertraut bist, ist das gleich doppelt schwierig zu verstehen. Hier mal ein Crashkurs:

    - COM arbeitet mit Interfaces und sogenannten Co-Klassen, die die Interfaces implementieren.
    - Jedes Interface und jede Coklasse hat einen weltweit eindeutigen Bezeichner in Form einer GUID (IID = InterfaceID, CLSID = ClassID).
    - Das, was in .NET "Object" ist, ist in COM das Interface "IUnknown".
    - Objekte werden in COM nicht mit "New", sondern mit CoCreateInstance unter Angabe der gewünschten CLSID erstellt
    - CoCreateInstance erstellt die richtige Coklasse und gibt IUnknown davon zurück, weil jedes Objekt IUknown implementiert
    - Dein korrekt typisiertes Objekt bekommst du, indem du IUnknown auf das richtige Interface castest, das geht mit QueryInterface()
    - Da COM-Referenzen eigentlich unverwaltete Zeiger sind, müssen sie mit Release() freigegeben werden.

    Alles, was jetzt bleibt, ist Interop-Kram, also das, was man in .NET-Syntax machen muss, um das native Verhalten von COM zu erreichen. Dazu zählt z.B. das Konvertieren von Datentypen zwischen der .NET-Welt und der COM-Welt oder das Casten einer IUnknown-Referenz (Pointer!) in ein .NET-"Object".
    Gruß
    hal2000