我遇到了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,我也想知道为什么这段代码不起作用。
提前感谢您提供的任何帮助。
答案 0 :(得分:0)
答案是使用NativeWindow类和SHBindToObject来获取IContextMenu2和IContextMenu3接口。