p / Invoke中的IContextMenu3 HandleMenuMsg2

时间:2013-12-23 19:14:52

标签: c# vb.net winapi pinvoke

我遇到了p / Invoke和上下文菜单系统的问题。我能够调出并获取菜单句柄并正确填充菜单项,但是当我尝试获取IContextMenu2和IContextMenu3对象时,虽然我可以获取它们,但它们的消息处理程序似乎没有做任何事情,除了可能会崩溃我的窗口过程。

以下是我所拥有的IContextMenu,IContextMenu2和IContextMenu3接口的C#声明,我将其添加到Windows API代码包中:

[ComImport,
Guid("000214e4-0000-0000-c000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IContextMenu
{

    [PreserveSig]
    HResult QueryContextMenu( 
        /* [annotation][in] */
        [In]  IntPtr hmenu,
        /* [annotation][in] */ 
        [In]  UInt16 indexMenu,
        /* [annotation][in] */ 
        [In]  uint idCmdFirst,
        /* [annotation][in] */ 
        [In]  uint idCmdLast,
        /* [annotation][in] */ 
        [In]  uint uFlags);


    HResult InvokeCommand(
        /* [annotation][in] */
        [In]  CMINVOKECOMMANDINFOPTR pici);
    [PreserveSig]

    HResult InvokeCommand(
        /* [annotation][in] */
        [In]  CMINVOKECOMMANDINFO pici);

    HResult InvokeCommand(
        /* [annotation][in] */
        [In]  IntPtr pici);

    [PreserveSig]
    HResult GetCommandString( 
        /* [annotation][in] */ 
        [In]  IntPtr idCmd,
        /* [annotation][in] */ 
        [In]  uint uType,
        /* [annotation][in] */ 
        [In, Out] ref uint pReserved,
        /* [annotation][out] */ 
        [In] IntPtr pszCommand,
        /* [annotation][in] */ 
        [In] uint cchMax);

};

[ComImport,
Guid("000214f4-0000-0000-c000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IContextMenu2 : IContextMenu
{

    [PreserveSig]
    HResult HandleMenuMsg(
        /* [annotation][in] */
        [In] uint uMsg,
        /* [annotation][in] */
        [In] IntPtr wParam,
        /* [annotation][in] */
        [In] IntPtr lParam);
}

[ComImport,
Guid("BCFCE0A0-EC17-11D0-8D10-00A0C90F2719"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IContextMenu3 : IContextMenu2
{
    [PreserveSig]
    HResult HandleMenuMsg2(
        /* [annotation][in] */
        [In] uint uMsg,
        /* [annotation][in] */
        [In] IntPtr wParam, 
        /* [annotation][in] */ 
        [In] IntPtr lParam,
        /* [annotation][out] */
        [Out]
        out IntPtr plResult);
}

这是窗口proc和VB端的公共变量:

Private _icx2 As IContextMenu2 = Nothing
Private _icx3 As IContextMenu3 = Nothing

Dim oldProc As WndProcDelegate
Dim rRet As IntPtr

Dim _hctx3 As Boolean = False
Dim _hctx2 As Boolean = False

Dim newProc _
    As New WndProcDelegate _
(Function(hwnd As IntPtr, _
          uMsg As UInteger, _
          wParam As IntPtr, _
          lParam As IntPtr) As IntPtr


     If _hctx3 Then
         rRet = 0
         If _icx3.HandleMenuMsg2(uMsg, wParam, lParam, rRet) = HResult.Ok Then
             Return rRet
         End If
     ElseIf _hctx2 Then
         If _icx2.HandleMenuMsg(uMsg, wParam, lParam) = HResult.Ok Then
             Return 0
         End If
     End If

     'Select Case uMsg
     '    Case WM_INITMENUPOPUP, WM_DRAWITEM, WM_MENUCHAR, WM_MEASUREITEM
     '        If _hctx3 Then
     '            rRet = 0
     '            _icx3.HandleMenuMsg2(uMsg, wParam, lParam, rRet)
     '            Return rRet
     '        End If

     'End Select

     Return oldProc(hwnd, uMsg, wParam, lParam)
 End Function)

正如你所看到的,我曾尝试按照MSDN关于这个问题的文章的规定去做,并且只关注相关的消息,现在我正在做这个改编,改编自Raymond Chen关于同一主题的文章

最后,这就是我如何在代码中激活弹出窗口的全部努力:

Dim handleWnd As New Form

Dim ip As IntPtr, _
    ip2 As IntPtr

' Set the new window proc for the new window handle.
oldProc = SetWindowLongPtr(handleWnd.Handle, GWL_WNDPROC, newProc)

' Call the helper function to get the context menu
GetUIObjectOfFile(handleWnd.Handle, fileName, New Guid(IID_IContextMenu), mnu)

' That did not work, abort.
If mnu Is Nothing Then Return

' Create a popup menu
hMenu = CreatePopupMenu
mnu.QueryContextMenu(hMenu, 0, 1, &H7FFF, 0)

' Get the COM interface for IContextMenu2
' 000214f4-0000-0000-c000-000000000046
ip = Marshal.GetIUnknownForObject(mnu)
Marshal.QueryInterface(ip, New Guid(IID_IContextMenu2), ip2)
_icx2 = Marshal.GetObjectForIUnknown(ip2)

If _icx2 IsNot Nothing Then _hctx2 = True

' Get the COM interface for IContextMenu3
' BCFCE0A0-EC17-11D0-8D10-00A0C90F2719
ip = Marshal.GetIUnknownForObject(mnu)
Marshal.QueryInterface(ip, New Guid(IID_IContextMenu3), ip2)
_icx3 = Marshal.GetObjectForIUnknown(ip2)

If _icx3 IsNot Nothing Then _hctx3 = True

mi = TrackPopupMenuEx(hMenu, TPM_RETURNCMD, x, y, handleWnd.Handle, Nothing)

' Revert the window proc
SetWindowLongPtr(handleWnd.Handle, GWL_WNDPROC, oldProc)

If _hctx3 Then
    _hctx3 = False
    _icx3 = Nothing
End If
If _hctx2 Then
    _hctx2 = False
    _icx2 = Nothing
End If

当我没有尝试运行菜单处理程序功能时,菜单显示得很好,除了缺少Share With和Open With菜单。填充发送到和添加到库的那些没有调用任何此行为。尝试调用此行为后,菜单的外观完全不可预测,但通常会导致图标在您将鼠标悬停在图标上时消失,并且根本不会填充任何菜单。

我有一个我用C ++编写的函数,它构成了我,我也可以使用IAssocHandler接口构建菜单,就像我一直在做的那样。我希望通过p / Invoke在.NET中完成本地操作,而不必诉诸C,我也想知道为什么这段代码不起作用。

提前感谢您提供的任何帮助。

1 个答案:

答案 0 :(得分:0)

答案是使用NativeWindow类和SHBindToObject来获取IContextMenu2和IContextMenu3接口。