Shell Extensions Interface Problem (ICopyHook Callback)

  • VB.NET

Es gibt 2 Antworten in diesem Thema. Der letzte Beitrag () ist von Serow.

    Shell Extensions Interface Problem (ICopyHook Callback)

    Guten Abend,
    ich versuche derzeit einen Copy Hook zu schreiben, da MS das CopyHook Beispiel im All-In-On Framework entfernt hatte habe ich die C++ Variante in .net umgeschrieben.
    Derzeit scheint es ein Problem mit dem Interface zu geben, da Shell Extension schwer zu Debuggen sind komme ich jetzt nicht weiter. Das einzige was ich sagen kann ist das der explorer die Extension über mscoree lädt, und diese eine Exception auswirft.
    Dachte zuerst es gibt ein Problem mit dem Namespace in VB alles in C# umgecodet, hat wenig gebracht. Ansonsten der Assembly ist Signiert und ist für x64 Compiled.

    Das C++ Bsp. Project, C# u. VB im Dateianhang

    Hier mal ein Ausschnitt, hoffe mal hier kann mir jemand weiterhelfen :whistling:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Runtime.InteropServices
    2. Imports Microsoft.Win32
    3. #Region "Shell Interfaces"
    4. <ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), _
    5. Guid("000214e8-0000-0000-c000-000000000046")> _
    6. Interface IShellExtInit
    7. <PreserveSig()> _
    8. Sub Initialize( _
    9. ByVal pidlFolder As IntPtr, _
    10. ByVal pDataObj As IntPtr, _
    11. ByVal hKeyProgID As IntPtr)
    12. End Interface
    13. <ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), _
    14. Guid("000214FC-0000-0000-C000-000000000046")> _
    15. Interface ICopyHook
    16. <PreserveSig()> _
    17. Function CopyCallback( _
    18. ByVal hwnd As IntPtr, _
    19. ByVal wFunc As UInteger, _
    20. ByVal wFlags As UInteger, _
    21. <MarshalAs(UnmanagedType.LPStr)> ByVal pszSrcFile As String, _
    22. ByVal dwSrcAttribs As UInteger, _
    23. <MarshalAs(UnmanagedType.LPStr)> ByVal pszDestFile As String, _
    24. ByVal dwDestAttribs As UInteger) As UInteger
    25. End Interface
    26. #End Region
    27. #Region "Enums & Structs"
    28. Public Enum eCOPYCALLBACK_STATUS As UInteger
    29. IDCANCEL = 2
    30. IDYES = 6
    31. IDNO = 7
    32. End Enum
    33. Public Enum eSHELL_FILE_OPERATIONS As UInteger
    34. FO_MOVE = 1
    35. FO_COPY = 2
    36. FO_DELETE = 3
    37. FO_RENAME = 4
    38. End Enum
    39. <StructLayout(LayoutKind.Sequential)> _
    40. Class SHFILEOPSTRUCT
    41. Public hwnd As Integer
    42. Public wFunc As Integer
    43. <MarshalAs(UnmanagedType.LPWStr)> _
    44. Public pFrom As String
    45. <MarshalAs(UnmanagedType.LPWStr)> _
    46. Public pTo As String
    47. Public fFlags As Integer
    48. <MarshalAs(UnmanagedType.Bool)> _
    49. Public fAnyOperationsAborted As Boolean
    50. Public hNameMappings As Integer
    51. <MarshalAs(UnmanagedType.LPWStr)> _
    52. Public lpszProgressTitle As String
    53. End Class
    54. #End Region
    55. #Region "Shell Registration"
    56. Friend Class ShellExtReg
    57. ''' <summary>
    58. ''' Register the copy hook handler.
    59. ''' </summary>
    60. ''' <param name="name"></param>
    61. ''' <param name="clsid"></param>
    62. ''' <remarks></remarks>
    63. Public Shared Sub RegisterShellExtContextMenuHandler( _
    64. ByVal name As String, _
    65. ByVal clsid As Guid)
    66. If clsid = Guid.Empty Then
    67. Throw New ArgumentException("CLSID must not be empty")
    68. End If
    69. Dim keyName As String = String.Format("Directory\shellex\CopyHookHandlers\{0}", name)
    70. Using key As RegistryKey = Registry.ClassesRoot.CreateSubKey(keyName)
    71. key.SetValue(Nothing, clsid.ToString("B"))
    72. End Using
    73. End Sub
    74. ''' <summary>
    75. ''' Unregister the copy hook handler.
    76. ''' </summary>
    77. Public Shared Sub UnregisterShellExtContextMenuHandler( _
    78. ByVal name As String)
    79. Dim keyName As String = String.Format("Directory\shellex\CopyHookHandlers\{0}", name)
    80. Registry.ClassesRoot.DeleteSubKeyTree(keyName, False)
    81. End Sub
    82. End Class
    83. #End Region

    Dateien
    • CopyHook.zip

      (988,03 kB, 310 mal heruntergeladen, zuletzt: )
    Hi.

    Zunächst: In VB ist alles möglich - C# ist nicht erforderlich :)

    Die Installation sollte funktionieren (auch wenn die Assembly eigentlich in den GAC gehört). Das Debugging geht am besten, wenn du in die Register- und Unregister-Funktionen den Aufruf "Debugger.Break()" setzt - dann kassierst du eine JIT-Exception, worüber du eine weitere VS-Instanz starten kannst, die automatisch den passenden Code lädt und dich im Einzelschritt durchgehen lässt. Ansonsten ist Debug.Print() ganz hilfreich, denn es reicht dann, wenn VS nur an explorer.exe angehängt ist. Die Ausgaben landen dann im Ausgabefenster.

    Ich habe das mal "from scratch" nachgestellt - das ist mein ganzer Code:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Runtime.InteropServices
    2. Imports Microsoft.Win32
    3. <ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214FC-0000-0000-c000-000000000046")> _
    4. Interface ICopyHook 'ICopyHookW
    5. 'STDMETHOD_(UINT,CopyCallback) (THIS_ HWND hwnd, UINT wFunc, UINT wFlags, LPCWSTR pszSrcFile, DWORD dwSrcAttribs,
    6. ' LPCWSTR pszDestFile, DWORD dwDestAttribs) PURE;
    7. <PreserveSig()> _
    8. Function CopyCallback(hwnd As IntPtr, wFunc As UInt32, wFlags As UInt32,
    9. <MarshalAs(UnmanagedType.LPWStr)> pszSrcFile As String, dwSrcAttribs As Int32,
    10. <MarshalAs(UnmanagedType.LPWStr)> pszDestFile As String, dwDestAttribs As Int32) As UInt32
    11. End Interface
    12. <Guid("28D7BB17-0BBA-4687-8C72-5D2DCCC5810D"), ClassInterface(ClassInterfaceType.None), ComVisible(True)> _
    13. Public Class MyCopyHook
    14. Implements ICopyHook
    15. <ComRegisterFunction()> _
    16. Private Shared Sub DllRegisterServer(t As Type)
    17. Using key As RegistryKey = Registry.ClassesRoot.CreateSubKey("Directory\Shellex\CopyHookHandlers\MyCopyHook")
    18. key.SetValue("", t.GUID.ToString("B"))
    19. End Using
    20. End Sub
    21. <ComUnregisterFunction()> _
    22. Private Shared Sub DllUnregisterServer(t As Type)
    23. Using key As RegistryKey = Registry.ClassesRoot.OpenSubKey("Directory\Shellex\CopyHookHandlers", True)
    24. key.DeleteSubKey("MyCopyHook")
    25. End Using
    26. End Sub
    27. Private Const IDCANCEL As UInt32 = 2
    28. Private Const IDYES As UInt32 = 6
    29. Private Const IDNO As UInt32 = 7
    30. Public Function CopyCallback(hwnd As System.IntPtr, wFunc As UInt32, wFlags As UInt32, pszSrcFile As String, dwSrcAttribs As Integer, pszDestFile As String, dwDestAttribs As Int32) As UInt32 Implements ICopyHook.CopyCallback
    31. Debug.Print("CopyCallback")
    32. Return IDYES
    33. End Function
    34. End Class

    Die Debugausgabe in VS sieht dann so aus (beim Kopieren einer Datei im Explorer):
    Spoiler anzeigen

    Quellcode

    1. "explorer.exe" (Verwaltet (v4.0.30319)): "C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll" geladen
    2. "explorer.exe" (Verwaltet (v4.0.30319)): "C:\Windows\Microsoft.Net\assembly\GAC_64\CopyHook\v4.0_1.0.0.0__7d2c57e7301f8ce1\CopyHook.dll" geladen, Symbole geladen.
    3. "explorer.exe" (Verwaltet (v4.0.30319)): "C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll" geladen
    4. "explorer.exe" (Verwaltet (v4.0.30319)): "C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll" geladen
    5. "explorer.exe" (Verwaltet (v4.0.30319)): "C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll" geladen
    6. CopyCallback
    7. Der Thread '<Kein Name>' (0x165c) hat mit Code 0 (0x0) geendet.
    8. Das Programm "[5152] explorer.exe: Verwaltet (v4.0.30319)" wurde mit Code 0 (0x0) beendet.

    Du siehst also: Es funktioniert, ohne eine Exception zu werfen. Da du das ICopyHook-Interface exakt gleich deklariert hast, muss das Problem woanders liegen. Was prinzipiell ganz schlecht ist: Debuggen mit MsgBox(). Du bekommst ein hwnd übergeben, das du zum "Anzeigen von UI-Elementen" verwenden sollst (laut MSDN). Vielleicht funkt die Messagebox da dazwischen (CreateWindow scheitert irgendwo tief im System). Benutze einfach Debug.Print() - das sollte völlig ausreichen.

    Wenn du die Exception finden willst, hilft dir WinDbg. Lade die Symbole, hänge ihn an den explorer an und warte auf die Exception. Danach kannst du dir mit kb den Stacktrace ansehen, was schon ausreichen dürfte, um den Fehler einzugrenzen.
    Gruß
    hal2000
    Danke für die Antwort, das Problem war eine Kombination aus dem falsch deklarierten COM Interface und ner falschen Einstellung in den Projekt-Eigenschaften. :D

    Noch zum Debuggen, hier lag es dran das wenn VS am explorer angehängt wird die Extension aus GAC geladen wird aber die Program Debug Database im Ausgabeverzeichnis liegt,
    STRG + ALT + U und dann die pdb hinzufügen oder direkt in GAC ziehen.