阿里巴巴|IContextMenu第十一部分:组合扩展的实现( 二 )



经过一些初步调整后我们找到了命令标识符 , 然后我们分三个步骤进行调用 。
首先 , 如果命令是作为字符串发送的 , 那么这是最简单的情况 。 我们遍历所有包含的上下文菜单 , 询问每个菜单是否识别命令 。 一旦确定 , 我们就可以执行对应的操作了 。 如果没有人知道 , 那么我们耸耸肩说我们也不知道 。
其次 , 如果分派的命令是序数 , 我们要求 ReduceOrdinal 找出它属于哪个包含的上下文菜单处理程序 。
第三 , 我们重写了 CMINVOKECOMMANDINFO 结构 , 使其适用于包含的上下文菜单处理程序 。 这意味着更改 lpVerb 成员和可能的 lpVerbW 成员以包含相对于包含的上下文菜单处理程序而不是相对于容器的新菜单偏移 。 由于 Unicode 动词 lpVerbW 可能不存在 , 因此这会稍微复杂一些 。 我们将其隐藏在 pszVerbWFake 局部变量后面 , 如果没有真正的 lpVerbW , 它就会出现 。
好的 , 现在你已经了解了将方法调用分发到相应包含的上下文菜单背后的基本思想 , 其余的应该相对容易一些了 。
下面是GetCommandString的参考实现:

GetCommandString 方法遵循与 InvokeCommand 相同的三步模式 。
首先 , 通过调用每个包含的上下文菜单处理程序来分派任何基于字符串的命令 , 直到有人接受它 。如果没有人这样做 , 则拒绝该命令 。(注意 GCS_VALIDATE 的特殊处理 , 它需要 S_FALSE 而不是错误代码 。 )
其次 , 如果命令由 ordinal 指定 , 则要求 ReduceOrdinal 找出它属于哪个包含的上下文菜单处理程序 。
第三 , 将缩减的命令传递给适用的包含上下文菜单处理程序 。
最后的方法通过一个小的帮助函数来实现:

此辅助函数采用 IContextMenu 接口指针并尝试调用
IContextMenu3::HandleMenuMsg2; 如果失败 , 则尝试 IContextMenu2::HandleMenuMsg; 如果这也失败了 , 那么它就放弃了 。
有了这个辅助函数 , 最后两个方法小菜一碟 。


IContextMenu2::HandleMenuMsg 方法只是 IContextMenu3::HandleMenuMsg2 方法的转发器:

而IContextMenu3::HandleMenuMsg2 方法只是遍历上下文菜单处理程序列表 , 询问每个处理程序是否希望处理该命令 , 并在最终执行时停止 。
有了这个复合菜单类 , 我们可以在我们的示例程序中通过将“真实”上下文菜单与我们的 CTopContextMenu 组合在一起来展示它 , 从而展示如何将多个上下文菜单组合成一个大的上下文菜单 , 如下图所示:

此函数通过创建两个包含的上下文菜单处理程序来构建复合 , 然后创建一个包含它们的复合上下文菜单 。我们可以通过对我们上次调整的 OnContextMenu 函数进行相同的一行调整来使用这个函数:

请注意 , 使用此复合上下文菜单 , 我们在窗口标题中更新的菜单帮助文本跨越原始文件上下文菜单和“顶部”上下文菜单 。 来自任何一方的命令也被成功调用 。
与第九部分中的方法相比 , 此方法的价值在于你不再需要在两段代码之间协调上下文菜单的自定义 。 根据之前的技术 , 你必须确保更新菜单帮助文本的代码与添加自定义命令的代码同步 。
在新方法下 , 所有自定义都保存在一个地方(在复合上下文菜单内的“顶部”上下文菜单中) , 因此窗口过程不需要知道发生了哪些自定义 。 如果有多个显示上下文菜单的点 , 一些未自定义 , 另一些以不同方式自定义 , 则会变得更有价值 。
好的 , 我认为现在在上下文菜单这个主题已经讲得差不多了 。 我希望你已经更好地了解它们的工作原理 , 如何利用它们 , 最重要的是 , 如何使用组合等技术对它们执行元操作(meta-operations) 。