(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...

(本系列文章由柠檬的(lc_mtt)原创,转载请注明出处,谢谢~)

接上一节:(C#)Windows Shell 外壳编程系列3 - 上下文菜单(iContextMenu)(一)右键菜单

上一节说到如何弹出 IShellFolder 的上下文菜单,也就是 IContextMenu。有时候我们需要在这个菜单上面,加入一些属于自己的菜单项。举个例子,你打开资源管理器,查看左边目录树的右键菜单,会发现顶层多了一个折叠/展开的菜单项。好,我们也动手来加入这个菜单项。

修改例子3,我们找到 QueryContextMenu 处,这时候提供了一个菜单句柄:

(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...//提供一个弹出式菜单的句柄
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...IntPtr contextMenu = API.CreatePopupMenu();
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...iContextMenu.QueryContextMenu(contextMenu, 0,
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...    API.CMD_FIRST, API.CMD_LAST, CMF.NORMAL | CMF.EXPLORE);


然后增加以下代码:

(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令.../////////////////////////增加一个自定义菜单
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...string topInvoke = Tree1.SelectedNode.IsExpanded ? "折叠(&A)" : "展开(&A)";
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...MFT extraFlag = (Tree1.SelectedNode.Nodes.Count > 0) ? 0 : MFT.GRAYED;
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...API.InsertMenu(contextMenu, 0, MFT.BYPOSITION | extraFlag,
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...    (int)(API.CMD_LAST+1), topInvoke);
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...//增加分隔线
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...API.InsertMenu(contextMenu, 1, MFT.BYPOSITION | MFT.SEPARATOR, 0, "-");
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...//把第一项菜单设置为默认菜单,也就是加粗
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...API.SetMenuDefaultItem(contextMenu, 0, true);
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令.../////////////////////////


这里我们用到了 InsertMenu 这个 API:

(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...[DllImport("user32",
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...            SetLastError = true,
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...            CharSet = CharSet.Auto)]
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...        public static extern bool InsertMenu(
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...            IntPtr hmenu,
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...            uint uPosition,
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...            MFT uflags,
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...            uint uIDNewItem,
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...            [MarshalAs(UnmanagedType.LPTStr)]
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...            string lpNewItem);


参数2表示增加菜单项的位置,从0开始。
参数3表示flag,这里提供了菜单状态,以及位置的计算方法,它是一个枚举:

(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...public enum MFT


MF_BYPOSITION 表示位置的计算方法是使用索引项,第一个菜单就是0,第二个菜单就是1,如此类推...

参数4表示命令值。我们可以根据这个命令值来执行对应的功能。

然后就可以弹出菜单了:

(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...//弹出菜单
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...uint cmd = API.TrackPopupMenuEx(contextMenu,TPM.RETURNCMD,
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...MousePosition.X, MousePosition.Y, this.Handle, IntPtr.Zero);


可以看到弹出菜单的效果。当然,我们还必须做点事情来响应这个菜单的执行:

(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...//获取命令序号,执行菜单命令
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...if (cmd >= API.CMD_FIRST)
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...{
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...    (C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...    //自定义菜单命令
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...    if (cmd == API.CMD_LAST + 1)
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...    {
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...        if (Tree1.SelectedNode.IsExpanded)
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...            Tree1.SelectedNode.Collapse();
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...        else
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...            Tree1.SelectedNode.Expand();
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...    }
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...}


如图:

(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...

执行菜单命令

能不能不弹出菜单直接调用菜单项相应的命令?答案是肯定的。

大家还记得怎么显示一个文件或文件夹的属性对话框吗?

Yes,用ShellExecuteEx并指定SHELLEXECUTEINFO的lpVerb域为properties就可,但是这种方法只能查看一个文件的属性,怎么同时查看多个的?

要知道ShellExecuteEx查看文件属性最终也是靠IContextMenu帮忙的,所以答案还是在IContextMenu上,我们只要在调用GetUIObjectOf时把想查看的文件或文件件的PIDL做为参数传进去,然后直接调用InvokeCommand方法就OK啦。

当然,我们做的例子,还是弹出一个对象的属性,靠你自己修改了。

我们必须先得到 IContextMenu 接口:

(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...//得到 IContextMenu 接口
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...IntPtr iContextMenuPtr = IntPtr.Zero;
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...iContextMenuPtr = IParent.GetUIObjectOf(IntPtr.Zero, (uint)pidls.Length,
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...    pidls, ref Guids.IID_IContextMenu, out iContextMenuPtr);
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...IContextMenu iContextMenu = (IContextMenu)Marshal.GetObjectForIUnknown(iContextMenuPtr);

但我们不弹出这个菜单,仅仅是调用 InvokeCommand 来执行命令而已:

(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...//直接执行命令
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...CMINVOKECOMMANDINFOEX invoke = new CMINVOKECOMMANDINFOEX();
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...invoke.cbSize = Marshal.SizeOf(typeof(CMINVOKECOMMANDINFOEX));
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...invoke.lpVerb = Marshal.StringToHGlobalAnsi("properties");
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...invoke.lpDirectory = string.Empty;
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...invoke.fMask = 0;
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...invoke.nShow = 1;
(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...iContextMenu.InvokeCommand(ref invoke);

关于verb的更多信息请参考MSDN。我这里做的是执行“属性”,如果你要执行其他命令,或者按照索引来执行,也是可以的。这里不做深入研究。

(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...

源代码:/Files/lemony/WinShell4.rar

我正在考虑下一节是讲图标,还是继续讲iContextMenu。大家也知道,某个文件的右键菜单里面,往往会有几个 winrar 的选项,还带着可爱的图标。

(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...

很有可能下一节就讲述如何在C#中也实现这样的效果哦。希望大家多多支持^_^。