IContextMenu第十一部分:组合扩展的实现

现在,我们已经有两个菜单处理器了,一个是来自外壳的上下文菜单,另一个是我们自定义的菜单(包含两个自定义菜单项),下面我们看看如何将它们组合在一起。具体来说,我们会使用到一个组合上下文处理器来实现。

现在,我们已经有两个菜单处理器了,一个是来自外壳的上下文菜单,另一个是我们自定义的菜单(包含两个自定义菜单项),下面我们看看如何将它们组合在一起。具体来说,我们会使用到一个组合上下文处理器来实现。

组合上下文菜单的核心思想是将多个上下文菜单组合到一个处理器中,并使用菜单标识符的偏移来对菜单项进行路由和定位。
请看下面的代码。

IContextMenu第十一部分:组合扩展的实现

IContextMenu第十一部分:组合扩展的实现

在上面的代码中,我们使用到了一个结构体CONTEXTMENUINFO,它包含了菜单中的一些信息。我们不仅需要上下文菜单指针,而且也需要被IContextMenu::QueryContextMenu使用到的菜单项的数量。
下面的代码演示了我们如何实现这个类。

IContextMenu第十一部分:组合扩展的实现

因为对于一个C++对象来说,它的构造函数不会失败,因此在构造过程中如何处理失败有多种设计惯例。我在这里使用的一个设计是,将大部分工作放在Initialize方法中,如果初始化失败,该方法可以返回适当的错误代码。(请注意,这里我假设new操作符不会抛出异常)

我们的初始化函数分配了一堆CONTEXTMENUINFO结构并复制IContextMenu指针以妥善保管。(注意 m_ccmi 成员直到我们知道内存分配成功后才会设置。)

下面是类的析构函数实现:

IContextMenu第十一部分:组合扩展的实现

接下来是Create方法的实现:

IContextMenu第十一部分:组合扩展的实现

因为我们的接口继承自IUnknown接口,所以还需要实现IUnknown接口的三个方法,如下图所示:

IContextMenu第十一部分:组合扩展的实现

接下来,是一个我们比较感兴趣的方法实现:IContextMenu::QueryContextMenu。

IContextMenu第十一部分:组合扩展的实现

我们依次要求每个包含的上下文菜单将其命令添加到上下文菜单中。你可以在此处看到 IContextMenu::QueryContextMenu 方法返回值的原因之一。 通过告诉容器使用了多少个菜单标识符,容器知道还剩下多少给其他人使用。 然后容器返回所有上下文菜单使用的菜单标识符总数。
IContextMenu::QueryContextMenu 方法返回值的解释请看下面的帮助函数:

IContextMenu第十一部分:组合扩展的实现

此方法采用菜单偏移量并计算出它属于哪个包含的上下文菜单,使用来自 IContextMenu::QueryContextMenu 的返回值来决定如何划分标识符空间。 pidCmd 参数是输入/输出。 进入函数时,它是复合上下文菜单的菜单偏移量; 退出函数时,它是包含的上下文菜单的菜单偏移量,通过 ppcmi 参数返回。

IContextMenu::InvokeCommand 可能是最复杂的,因为它需要支持四种不同的命令调度方式,请看下图:

IContextMenu第十一部分:组合扩展的实现

经过一些初步调整后我们找到了命令标识符,然后我们分三个步骤进行调用。

首先,如果命令是作为字符串发送的,那么这是最简单的情况。我们遍历所有包含的上下文菜单,询问每个菜单是否识别命令。一旦确定,我们就可以执行对应的操作了。如果没有人知道,那么我们耸耸肩说我们也不知道。

其次,如果分派的命令是序数,我们要求 ReduceOrdinal 找出它属于哪个包含的上下文菜单处理程序。

第三,我们重写了 CMINVOKECOMMANDINFO 结构,使其适用于包含的上下文菜单处理程序。这意味着更改 lpVerb 成员和可能的 lpVerbW 成员以包含相对于包含的上下文菜单处理程序而不是相对于容器的新菜单偏移。由于 Unicode 动词 lpVerbW 可能不存在,因此这会稍微复杂一些。我们将其隐藏在 pszVerbWFake 局部变量后面,如果没有真正的 lpVerbW,它就会出现。

好的,现在你已经了解了将方法调用分发到相应包含的上下文菜单背后的基本思想,其余的应该相对容易一些了。

下面是GetCommandString的参考实现:

IContextMenu第十一部分:组合扩展的实现

GetCommandString 方法遵循与 InvokeCommand 相同的三步模式。

首先,通过调用每个包含的上下文菜单处理程序来分派任何基于字符串的命令,直到有人接受它。 如果没有人这样做,则拒绝该命令。 (注意 GCS_VALIDATE 的特殊处理,它需要 S_FALSE 而不是错误代码。)
其次,如果命令由 ordinal 指定,则要求 ReduceOrdinal 找出它属于哪个包含的上下文菜单处理程序。
第三,将缩减的命令传递给适用的包含上下文菜单处理程序。

最后的方法通过一个小的帮助函数来实现:

IContextMenu第十一部分:组合扩展的实现

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

IContextMenu第十一部分:组合扩展的实现

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

IContextMenu第十一部分:组合扩展的实现

而 IContextMenu3::HandleMenuMsg2 方法只是遍历上下文菜单处理程序列表,询问每个处理程序是否希望处理该命令,并在最终执行时停止。

有了这个复合菜单类,我们可以在我们的示例程序中通过将“真实”上下文菜单与我们的 CTopContextMenu 组合在一起来展示它,从而展示如何将多个上下文菜单组合成一个大的上下文菜单,如下图所示:

IContextMenu第十一部分:组合扩展的实现

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

IContextMenu第十一部分:组合扩展的实现

请注意,使用此复合上下文菜单,我们在窗口标题中更新的菜单帮助文本跨越原始文件上下文菜单和“顶部”上下文菜单。来自任何一方的命令也被成功调用。

与第九部分中的方法相比,此方法的价值在于你不再需要在两段代码之间协调上下文菜单的自定义。根据之前的技术,你必须确保更新菜单帮助文本的代码与添加自定义命令的代码同步。

在新方法下,所有自定义都保存在一个地方(在复合上下文菜单内的“顶部”上下文菜单中),因此窗口过程不需要知道发生了哪些自定义。如果有多个显示上下文菜单的点,一些未自定义,另一些以不同方式自定义,则会变得更有价值。

好的,我认为现在在上下文菜单这个主题已经讲得差不多了。我希望你已经更好地了解它们的工作原理,如何利用它们,最重要的是,如何使用组合等技术对它们执行元操作(meta-operations)。

你还可以使用上下文菜单执行其他一些操作,你可以自行进行研究。例如,可以使用 IContextMenu::GetCommandString 方法遍历菜单并为每个项目获取与语言无关的命令。如果你想删除“删除”选项,这很方便:可以查找与语言无关的名称为“删除”的命令。当用户更改语言时,此名称不会更改;它将永远是英文的。

正如我们之前所注意到的,你需要注意许多上下文菜单处理程序并没有完全实现 IContextMenu::GetCommandString 方法,因此可能会有一些你根本无法获得名称的命令。这个要特别注意。

总结

总算是完成这个系列了,我可以去开一瓶香槟休息一下了。
感谢各位老哥的一路陪伴。

最后

Raymond Chen的《The Old New Thing》是我非常喜欢的博客之一,里面有很多关于Windows的小知识,对于广大Windows平台开发者来说,确实十分有帮助。
本文来自:《How to host an IContextMenu, part 11 – Composite extensions – composition》

最近我写了个东西

正如你们所知道的,拓扑梅尔智慧办公平台(Topomel Box)是一款绿色软件,主要面向经常使用电脑的朋友。它提供了各种提升办公效率的小功能,同时操作上尽可能地简单方便。
我想:你值得拥有。

IContextMenu第十一部分:组合扩展的实现

内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/101617.html

(0)

相关推荐

  • ios15.1.1正式版已经推送,到底值不值得更新?

    目前苹果已经推送15.1.1正式版了,许多果粉朋友们表示现在只是ios15.1正式版,没有收到系统的任何推送,非常的疑惑这到底是怎么回事。

    科技 2021年11月24日
  • 负债1800亿,负债700亿

    随着疫情的来临,很多人才发现存款的必须性,要不然谁知道在何时会再出现情况,却发现自己连钱都没有。而我国在全球都是著名的“储蓄大国”,多年来,国人都有存钱的习惯,毕竟很多家庭来说,存款不单单是为了家庭也是为了孩子的未来。

    生活 2021年10月23日
  • 为何说贵人语迟有什么道理吗,小孩说话晚是贵人语迟

    民间有一说,说话晚的孩子智商高,所谓“贵人语迟”。其实不然!智力发育迟缓、交流障碍等都是导致儿童语言发育落后的可能原因,及早到医院检查才是上策。

    生活 2021年10月22日
  • 爱奇艺发布2021年第三季度财报

    36氪获悉,爱奇艺发布2021年第三季度财报。财报显示,该季度爱奇艺营收76亿元,上年同期营收72亿元,市场预期76.03亿元。其中,会员服务营收为43亿元人民币同比增长8%;在线广告服务营收17亿元,同比下降10%。内容发行营收6.271亿元,同比增长60%。其他营收实现收入10亿元,同比增长3%。

    科技 2021年11月17日
  • 氟桂利嗪、尼莫地平、倍他司汀对脑血管疾病的治疗效果如何?注意什么?

    氟桂利嗪为桂利嗪的二氟化衍生物,属于选择性钙离子拮抗剂,可阻止过量钙离子进人血管平滑肌细胞,引起血管扩张,对脑血管的选择性较好,而对心肌血管作用较差,因此对血压、心率的影响小。对血管收缩物质引起的血管收缩有持久的抑制作用,对基底动脉和颈内动脉作用更明显。可抑制脑组织缺血缺氧引起的钙超载,保护脑组织,能透过血脑屏障,减轻脑细胞缺血缺氧性损伤。对血管内皮细胞因缺氧引起的钙超载有防治作用,保护血管内皮细胞的完整性。可增加耳蜗内辐射小动脉血流量,改善前庭器官微循环,对眼球震颤和眩晕起到抑制作用。氟桂利嗪通过阻断钙超载而防止阵发性去极化改变和细胞癫痫放电。还可抑制缺血和酸中毒后红细胞摄钙过多产生的锯齿状改变,降低细胞脆性,增加红细胞的变形能力,从而改善缺血缺氧区红细胞淤滞状态而改善微循环。还具有抗癫痫、抗组胺和镇静作用。用于脑动脉缺血性疾病(如脑动脉硬化、短暂性脑缺血发作、脑血栓形成、脑栓塞和脑血管痉挛)、由前庭刺激或脑缺血引起的头晕、耳鸣、眩晕、防治血管性偏头痛、辅助治疗癫痫。慢性肾衰竭患者可以使用氟桂利嗪,并且有改善肾功能的可能。氟桂利嗪对降低急性缺血性脑卒中的发病率和死亡率无效。

    生活 2021年11月15日