好的,看起来可以将自定义菜单项添加到 Windows Shell 上下文菜单中,就在您的应用程序中(无需执行任何影响整个 Windows 系统的操作)。下面是我所做的。请记住,此答案应在首先提到的 MSDN 代码的上下文中进行。这是链接:
MSDN 上的 ShellContextMenu 类
在那个类中,特别是 methodShowContextMenu
是对 的调用QueryContextMenu
,我了解到它的目的是用项目填充菜单——在这种情况下,是要显示其上下文菜单的文件/文件夹的适当 Windows Shell 菜单项。在那次调用之后,我添加了以下代码来添加子菜单和分隔符:
var mii = new MENUITEMINFO();
mii.fMask = MIIM.SUBMENU | MIIM.STRING | MIIM.FTYPE | MIIM.ID | MIIM.STATE | MIIM.BITMAP;
mii.fState = MFS.ENABLED;
mii.fType = MFT.STRING;
mii.wID = 0; // Application-defined value that identifies the menu item.
mii.hSubMenu = **subMenu**.Handle;
mii.dwTypeData = Program.ShortTitle;
mii.cch = mii.dwTypeData.Length;
var bmp = new System.Drawing.Icon(ac.Properties.NeutralResources.MyAppIcon, 16, 16).ToBitmap();
this.hMyAppSubmenuIcon = bmp.GetHbitmap();
mii.hbmpItem = this.hMyAppSubmenuIcon;
mii.cbSize = Marshal.SizeOf(typeof(MENUITEMINFO));
var success = InsertMenuItem(pMenu, 0, true, ref mii);
mii = new MENUITEMINFO();
mii.fMask = MIIM.FTYPE;
mii.fType = MFT.SEPARATOR;
mii.cbSize = Marshal.SizeOf(mii);
success = InsertMenuItem(pMenu, 1, true, ref mii);
subMenu的类型为System.Windows.Forms.ContextMenu
。这样,整个上下文菜单有点混合,由托管和非托管菜单项组成。到目前为止,我认为这没有问题;它只是意味着必须以不同的方式处理两种类型菜单项的选择,如下所述......
将托管项插入菜单后,该ShowContextMenu
方法调用TrackPopupMenu
. 对于 Shell 菜单项,已编写的类负责处理它们的选择。对于我自己的菜单项,我不得不采取额外的步骤,因为 TrackPopupMenu
它是一个 Windows API 函数,因此它不能很好地处理托管子菜单项的Click
事件。您可以将Click
事件处理程序连接到您的子菜单项,但是当从 Shell 上下文菜单中选择它们时,它们的Click
事件不会触发。我仍然连接他们Click
,因为有时我只是自己显示我的托管菜单。
为了响应来自 Shell 上下文菜单的托管菜单项选择,我使用了 的返回值TrackPopupMenu
,它是所选菜单项的资源 ID。这就是事情变得有点迂回的地方。创建托管上下文菜单时,每个菜单项都有一个索引。您可以将此与 Windows API 函数GetMenuItemID一起使用,该函数返回菜单项的资源 ID。我从托管ContextMenu
类继承并封装了一个Dictionary
帮助我从这个资源 ID 映射到菜单项,以便稍后在调用TrackPopupMenu
. 出于我的目的,这就是调用处理程序所需的全部内容,因为在我的应用程序中,我使用命令模式并将命令对象存储在菜单项的Tag
创建菜单时的属性。(一旦字典给了我正确的菜单项,我就可以通过从 中提取命令对象来执行相应的处理程序Tag
)。
这已经运行了好几天了。如果有人看到漏洞,例如我应该清理的非托管资源,请提及。