|IContextMenu第一部分:基本介绍


|IContextMenu第一部分:基本介绍
文章图片

|IContextMenu第一部分:基本介绍

网络上有很多技术文档描述了如何在资源管理器的上下文菜单中插入自己的菜单 , 并实现一个上下文菜单的提供者(context menu provider) , 你可以找到一些资料 , 教你如何承载(host)一个上下文菜单 。
(今天这篇文章是接下来的11篇系列文章的第一篇 , 是的 , 你没有看错 , 关于IContextMenu这一主题 , 我会写上11篇文章 。 各位读者 , 如果你来到我的博客 , 主要是想看看我写的一些历史小故事的话 , 抱歉了啊 。 当然 , 我也会在间歇的时间中 , 写一些其他主题的文章 , 以供你们消遣)
对于IContextMenu接口的使用 , 一般采用如下的模式:
> 创建 。
> IContextMenu::QueryContextMenu这个方法可以用来初始化上下文菜单 。 在此方法调用期间 , 上下文菜单会根据你传入的标志位参数 , 来决定哪些菜单条目会显示在菜单中 。
> 使用
IContextMenu::GetCommandString方法来显示菜单或者选择一个菜单命令来准备执行 。 另外两个方法 , IContextMenu2::HandleMenuMsg 和 IContextMenu3::HandleMenuMsg2 则用来处理用户界面交互 。
> 使用
IContextMenu::InvokeCommand方法来执行菜单命令 。
下面我们来从实现IContextMenu的角度看看底层都做了些什么 。
外壳会首先调用
IContextMenu::QueryContextMenu方法 。 它会传递一个HMENU句柄给这个方法 , 这样QueryContextMenu就可以向指定的菜单来添加菜单项 。 如果用户用鼠标指向了这些菜单项中的一个 , 则IContextMenu::GetCommandString方法会被调用 , 并获取该菜单项的帮助信息 , 这些帮助信息会被显示在资源管理器的状态栏上 。 如果用户点击了菜单项 , 则外壳会调用IContextMenu::InvokeCommand方法 。 然后该菜单项对应的处理例程会开始执行 。
作为IContextMenu的宿主 , 你需要做下面的事情:
IContextMenu宿主会首先调用
IContextMenu::QueryContextMenu方法 , 它会向此方法传递一个HMENU句柄以用来向上下文菜单中添加菜单项 。 如果用户用鼠标指向了菜单项中的一个 , 则IContextMenu::GetCommandString会被调用以获取该菜单项对应的帮助信息 , 并显示在宿主的状态栏上 。 如果用户点击了菜单项 , 则IContextMenu宿主会调用IContextMenu::InvokeCommand方法 。 IConectMenu所实现的处理例程会执行对应的菜单项动作 。
接下来几周的重点 。 我们会进一步解释上下文菜单的更多细节 。
【|IContextMenu第一部分:基本介绍】好了 , 让我们开始写代码吧 。
我们还是以之前的例子程序为基础 , 在这里我假定你已经熟悉了外壳命名空间和pidls , 所以不会重点解释它们 , 我会将重点放在上下文菜单部分 。
上述代码定义了一个简单函数 , 该函数接受一个路径并从中获取一个外壳UI对象 。我们使用 SHParseDisplayName 将路径转换为 pidl , 然后使用 SHBindToParent 绑定到 pidl 的父级 , 然后使用
IShellFolder::GetUIObjectOf 向父级询问子级的 UI 对象 。
(辅助函数 SHParseDisplayName 和 SHBindToParent 不会做任何你自己无法完成的事情 。 它们只是为你节省了一些输入 。 一旦你开始使用外壳命名空间 , 你就可以像这样建立一个个小函数库 。 )
从上述代码得知 , 我们要做的就是在用户右键单击时调用文件上的“播放”动作 。(为什么要右键单击?因为该程序的未来版本将显示上下文菜单 。 )
在上面的代码中 , 首先我们创建 IContextMenu , 然后通过调用
IContextMenu::QueryContextMenu 对其进行初始化 。请注意 , 即使我们不打算显示菜单 , 我们仍然需要创建一个弹出菜单 , 因为 IContextMenu::QueryContextMenu 需要打开 。然而 , 我们实际上并没有显示结果菜单; 我们不是要求用户从菜单中选择一个项目 , 而是为用户做出选择并选择“播放” , 填充 CMINVOKECOMMANDINFO 结构并调用它 。
但是我们怎么知道正确的动作是“播放”呢? 在这种情况下 , 我们知道是因为我们将文件硬编码为“clock.avi” , 并且我们知道 AVI 文件有一个“播放”动作 。但当然 , 这在一般情况下是行不通的 。在开始调用默认动作之前 , 让我们先采取更简单的步骤 , 询问用户要调用什么动作 。
如果上面的代码就是你真正想要的(在文件上调用一个固定的动作) , 那么你不需要浏览所有上下文菜单的东西 。上面的代码等效于调用 ShellExecuteEx 函数 , 即传递 SEE_MASK_INVOKEIDLIST 标志并指示你希望调用IContextMenu的相关方法 。