COM 组件设计与应用(十一)

2023-11-12

COM 组件设计与应用(十一
IDispatch 及双接口
的调用

作者:杨老师

下载源代码

一、前言
    前段时间,由于工作比较忙,没有能及时地写作。其间收到了很多网友的来信询问和鼓励,在此一并表示感谢。咳......我也需要工作来养家糊口呀......

    上回书介绍了两种方法来写自动化
(IDispatch)接口的组件程序,一是用 MFC 方式编写“纯粹”的 IDispatch 接口;二是用 ATL 方式编写“双接口”的组件。

二、IDispatch 接口和双接口
    使用者要想调用普通的 COM 组件功能,必须要加载这个组件的类型库(Type library)文件 tlb(比如在 VC 中使用 #import)。然而,在脚本程序中,由于脚本是被解释执行的,所以无法使用加载类型库的方式进行预编译。那么脚本解释器如何使用 COM 组件那?这就是自动化(IDispatch)组件大显身手的地方了。IDispatch
接口需要实现4个函数,调用者只通过这4个函数,就能实现调用自动化组件中所有的函数。这4个函数功能如下:
 

HRESULT GetTypeInfoCount(
    [out] UINT * pctinfo)
组件中提供几个类型库?当然一般都是一个啦。
但如果你在一个组件中实现了多个
IDispatch 接口,那就不一定啦(注1)
HRESULT GetTypeInfo(
    [in] UINT iTInfo,
    [in] LCID lcid,
    [out] ITypeInfo ** ppTInfo)
调用者通过该函数取得他想要的类型库。
幸好,在 99% 的情况下,我们都不用关心这两个函数的实现,因为 MFC/ATL 都帮我们完成了默认的一个实现,如果是自己完成函数代码,甚至可以直接返回 E_NOTIMPL 表示没有实现。(注2)
HRESULT GetIDsOfNames(
    [in] REFIID riid,
    [in,size_is(cNames)] LPOLESTR * rgszNames,

    [
in] UINT cNames,
    [in] LCID lcid,
    [out,size_is(cNames)] DISPID * rgDispId)
根据函数名称取得函数序号,为调用 Invoke() 做准备。
所谓函数序号,大家去观察双接口 IDL 文件和 MFC 的 ODL 文件,每一个函数和属性都会有
[id(序号)....] 这样的描述。
HRESULT Invoke(
    [in] DISPID dispIdMember,
    [in] REFIID riid,
    [in] LCID lcid,
    [in] WORD wFlags,
    [in,out] DISPPARAMS * pDispParams,
    [out] VARIANT * pVarResult,
    [out] EXCEPINFO * pExcepInfo,
    [out] UINT * puArgErr)
根据序号,执行函数。
使用 MFC/ATL 写的组件程序,我们也不必关心这个函数的实现。如果是自己写代码,则该函数类似如下实现:
switch(dispIdMember)
{
    case 1: .....; break;
    case 2: .....; break;
    ....
}
其实,就是根据序号进行分支调用啦。(注3)
 

    从 Invoke() 函数的实现就可以看出,使用 IDispatch 接口的程序,其执行效率是比较低的。ATL 从效率出发,实现了一种叫“双接口(dual)”的接口模式。下面我们来看看,到底什么是双接口:

comtut11pic1.jpg
图一、双接口
(dual)
结构示意图

    从上图中可以看出,所谓双接口,其实是在一个 VTAB 的虚函数表中容纳了三个接口(因为任何接口都是从
IUnknown 派生的,所以就不强调 IUnknown 了,叫做双接口)。我们如果从任意一个接口中调用 QueryInterface()得到另外的接口指针的话,其实,得到的指针地址都是同一个。双接口有什么好处那?答:好呀,多好呀,特别好呀......

 

使用方式 因为 所以
脚本语言使用组件 解释器只认识 IDispatch 接口 可以调用,但执行效率最低
编译型语言使用组件 它认识 IDispatch 接口 可以调用,执行效率比较低
编译型语言使用组件 它装载类型库后,就认识了 Ixxx 接口 可以直接调用 Ixxx 函数,效率最高啦

结论

双接口,既满足脚本语言的使用方便性,又满足编译型语言的使用高效性。
于是,我们写的所有的 COM 组件接口,都用双接口实现吗?
错!否!NO!
如果不是明确非要支持脚本的调用,则最好不要使用双接口,因为:

如果所有函数都放在一个双接口中,那么层次、结构、分类不清
如果使用多个双接口,则会产生其它问题(注4)
双接口、IDispatch接口只支持自动化的参数类型,使用受到限制,某些情况下很不方便喽
还有很多弊病呦,不过现在我想不起来喽......

三、使用方法
    如果你的开发环境是 vc6.0,那么我们使用第九回中的Simple6组件为例,快去下载呀......
    如果你的开发环境是 vc.net 2003,那么用第十回中的Simple8组件为例,快去下载呀......
    嘿嘿,其实不下载也没有关系,因为你只要下载本回的示例程序,里面已经包含了所需的组件。但使用前不要忘了去注册呀:regsvr32.exe simple6.dll regsvr32.exe simple8.dll
(注意别忘了输入组件的安装目录)。注册成功后,就可以使用了,使用方法有:
 

示例程序 自动化组件的使用方式 简要说明
示例0 在脚本中调用 第九回/第十回中,已经做了介绍
示例1 使用 API 方式调用 揭示 IDispatch 的调用原理,但傻子才去这么使用那,会累死了
示例2 使用 CComDispatchDriver 的智能指针包装类 比直接使用 API 方式要简单多啦,这个不错!
示例3 使用 MFC 装载类型库的包装方式 简单!好用!常用!但它本质上是使用 IDispatch 接口,所以执行效率稍差
示例4 使用 #import 方式加载类型库方式 #import 方式使用组件,咱们在第七回中讲过啦。常用!对双接口组件,直接调用自定义接口函数,不再经过 IDispatch,因此执行效率最高啦
示例x vbjavac#bcbdelphi....... 反正我不会,自己去请教高人去吧 :-(

示例一、IDispatch 调用原理篇

 
 示例二、CComDispatchDriver 智能指针包装类的使用方法 
 
 
 示例程序中使用了 Invoke2()函数,其实你根据不同的函数,还可以使用 Invoke0()Invoke1()InvokeN()PutProperty()GetProperty()......等等等,的确很方便。

示例三、加载类型库,产生包装类来使用
    这个方法使用更简单一些,如果你观察 MFC 帮你产生的包装类的实现,你就会发现,其实它调用的是 IDispatch 接口函数。使用 vc6.0 的朋友,步骤如下:
1、建立一个 MFC 的应用程序
2、开启 ClassWizard,执行 Add Class,选择
From a type library

comtut11pic2.jpg
图二、加载类型库

3、然后找到你要使用的组件文件 simple6.dll(tlb 文件也可以),选择接口后确认

comtut11pic3.jpg
图三、选择类型库中需要包装的接口

4、在适当的地方输入调用代码
 
     使用 vc.net 的朋友,步骤如下:
1、建立一个 MFC 的应用程序
2、执行菜单“添加\添加类”,选择 MFC 分类中的“类型库中的MFC类”

comtut11pic4.jpg
图四、添加类型库中的MFC类

3、选择组件文件 simple8.dll(tlb 文件),并选择需要包装的接口

comtut11pic5.jpg
图五、选择文件和接口

4、在适当的位置输入调用代码
 
 示例四、使用 #import 方式调用组件
    #import 方式在
第七回 中已经作过介绍,这里就不多罗嗦了。大家下载本回的示例程序后,自己去看吧。并且一定要掌握这个方法,因为它的运行效率是最快的呀。

四、小结
    留作业啦。在我们以前所实现的所有组件程序中,只添加了接口方法(函数),而没有添加接口属性(变量),你自己练习一下吧,很简单的,然后写个程序调用看看。其实对于 VC 来说,调用属性和调用方法没有太大的区别(vc 把属性包装为 GetXXX()/PutXXX()getXXX()/putXXX()的函数方式),但在另外一些语言中(比如脚本语言)则更方便,设置属性值是:对象.属性 = 变量或常量,获取属性值是:变量 = 对象.属性。
    本回书至此做一了断,更多组件设计和使用的知识,且听下回分解......
注1:多个自动化接口的实现方法,我们以后再说。
注2:将来介绍 ITypeLib::GetTypeInfo
() 的时候,大家再回味 IDispatch::GetTypeInfo()
吧。
注3:在后面介绍“事件”的时候,我们会自己真正去实现一个
IDispatch::Invoke()
函数。
注4:介绍多个双接口实现的时候,会谈到这个问题。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

COM 组件设计与应用(十一) 的相关文章

  • 堆栈溢出:堆栈空间中重复的临时分配?

    struct MemBlock char mem 1024 MemBlock operator const MemBlock b const return MemBlock global void foo int step 0 if ste
  • 总是使用 Final?

    我读过 将某些东西做成最终的 然后在循环中使用它会带来更好的性能 但这对一切都有好处吗 我有很多地方没有循环 但我将 Final 添加到局部变量中 它会使速度变慢还是仍然很好 还有一些地方我有一个全局变量final 例如android Pa
  • 如何在整个 ASP .NET MVC 应用程序中需要授权

    我创建的应用程序中 除了启用登录的操作之外的每个操作都应该超出未登录用户的限制 我应该添加 Authorize 每个班级标题前的注释 像这儿 namespace WebApplication2 Controllers Authorize p
  • Java执行器服务线程池[关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 如果我使用 Executor 框架在
  • Android 中麦克风的后台访问

    是否可以通过 Android 手机上的后台应用程序 服务 持续监控麦克风 我想做的一些想法 不断聆听背景中的声音信号 收到 有趣的 音频信号后 执行一些网络操作 如果前台应用程序需要的话 后台应用程序必须能够智能地放弃对麦克风的访问 除非可
  • 如何从指定日期获取上周五的日期? [复制]

    这个问题在这里已经有答案了 如何找出上一个 上一个 星期五 或指定日期的任何其他日期的日期 public getDateOnDay Date date String dayName 我不会给出答案 先自己尝试一下 但是 也许这些提示可以帮助
  • 玩!框架:运行“h2-browser”可以运行,但网页不可用

    当我运行命令时activator h2 browser它会使用以下 url 打开浏览器 192 168 1 17 8082 但我得到 使用 Chrome 此网页无法使用 奇怪的是它以前确实有效 从那时起我唯一改变的是JAVA OPTS以启用
  • 向现有 TCP 和 UDP 代码添加 SSL 支持?

    这是我的问题 现在我有一个 Linux 服务器应用程序 使用 C gcc 编写 它与 Windows C 客户端应用程序 Visual Studio 9 Qt 4 5 进行通信 是什么very在不完全破坏现有协议的情况下向双方添加 SSL
  • 获取 JVM 上所有引导类的列表?

    有一种方法叫做findBootstrapClass对于一个类加载器 如果它是引导的 则返回一个类 有没有办法找到类已经加载了 您可以尝试首先通过例如获取引导类加载器呼叫 ClassLoader bootstrapLoader ClassLo
  • 通过指向其基址的指针删除 POD 对象是否安全?

    事实上 我正在考虑那些微不足道的可破坏物体 而不仅仅是POD http en wikipedia org wiki Plain old data structure 我不确定 POD 是否可以有基类 当我读到这个解释时is triviall
  • 如何将带有 IP 地址的连接字符串放入 web.config 文件中?

    我们当前在 web config 文件中使用以下连接字符串 add name DBConnectionString connectionString Data Source ourServer Initial Catalog ourDB P
  • 将控制台重定向到 .NET 程序中的字符串

    如何重定向写入控制台的任何内容以写入字符串 对于您自己的流程 Console SetOut http msdn microsoft com en us library system console setout aspx并将其重定向到构建在
  • Firebase 添加新节点

    如何将这些节点放入用户节点中 并创建另一个节点来存储帖子 我的数据库参考 databaseReference child user getUid setValue userInformations 您需要使用以下代码 databaseRef
  • C# 成员变量继承

    我对 C 有点陌生 但我在编程方面有相当广泛的背景 我想做的事情 为游戏定义不同的 MapTiles 我已经像这样定义了 MapTile 基类 public class MapTile public Texture2D texture pu
  • 有没有办法为Java的字符集名称添加别名

    我收到一个异常 埋藏在第 3 方库中 消息如下 java io UnsupportedEncodingException BIG 5 我认为发生这种情况是因为 Java 没有定义这个名称java nio charset Charset Ch
  • IEnumreable 动态和 lambda

    我想在 a 上使用 lambda 表达式IEnumerable
  • JGit 检查分支是否已签出

    我正在使用 JGit 开发一个项目 我设法删除了一个分支 但我还想检查该分支是否已签出 我发现了一个变量CheckoutCommand但它是私有的 private boolean isCheckoutIndex return startCo
  • 如何将服务器服务连接到 Dynamics Online

    我正在修改内部管理应用程序以连接到我们的在线托管 Dynamics 2016 实例 根据一些在线教程 我一直在使用OrganizationServiceProxy out of Microsoft Xrm Sdk Client来自 SDK
  • 按日期对 RecyclerView 进行排序

    我正在尝试按日期对 RecyclerView 进行排序 但我尝试了太多的事情 我不知道现在该尝试什么 问题就出在这条线上适配器 notifyDataSetChanged 因为如果我不放 不会显示错误 但也不会更新 recyclerview
  • 如何实现仅当可用内存较低时才将数据交换到磁盘的写缓存

    我想将应用程序生成的数据缓存在内存中 但如果内存变得稀缺 我想将数据交换到磁盘 理想情况下 我希望虚拟机通知它需要内存并将我的数据写入磁盘并以这种方式释放一些内存 但我没有看到任何方法以通知我的方式将自己挂接到虚拟机中before an O

随机推荐

  • 如何使用下标遍历二维数组

    点击打开链接 int my 2d array 10 10 假定数组my 2d array 已经预先被填充了数据 int i j 遍历这个数组 for i 0 i lt 10 i 向下遍历各行 for j 0 j lt 10 j 穿越各列 p
  • Python:Unused import statement 解决方法

    Python 学习 21052501 1 Unused import statement 解决方法 Pycharm file 菜单下有Invalidate caches Restart菜单栏 点击清除缓存重新启动Pycharm即可 2 In
  • 反思深度学习与传统计算机视觉的关系

    来源 算法与数学之美 某种程度上 深度学习最大的优势就是自动创建没有人会想到的特性能力 如今 深度学习在众多领域都有一席之地 尤其是在计算机视觉领域 尽管许多人都为之深深着迷 然而 深网就相当于一个黑盒子 我们大多数人 甚至是该领域接受过培
  • 【C++进阶】二叉搜索树递归与非递归的模拟实现(附源码)

    一 什么是二叉搜索树 二叉搜索树又称二叉排序树 它或者是一棵空树 或者是具有以下性质的二叉树 根据二叉搜索树的性质 它的中序遍历结果就是一个升序列 二 二叉搜索树的模拟实现 节点 Node 在实现二叉搜索树之前 要先定义一个节点 成员变量包
  • Shiro权限框架-在线并发登录人数控制(9)

    1 实现原理 在实际开发中 我们可能会遇到这样的需求 一个账号只允许同时一个在线 当账号在其他地方登陆的时候 会踢出前面登陆的账号 那我们怎么实现 自定义过滤器 继承AccessControlFilter 使用redis队列控制账号在线数目
  • 基于Directshow的H.264流媒体播放器设计

    0引言 DirectsHow应用框架完成了流媒体处理的底层工作 使得编程者无需关心数据如何输入 以及处理完后如何输出 而只需关心如何对输入数据进行处理 H 264视频编解码标准具有高压缩比和优良的网络亲和性 被普遍认为是最有影响力的流媒体视
  • 变分推断

    变分推断 MATLAB实现变分贝叶斯蒙特卡洛模拟的贝叶斯推断 目录 变分推断 MATLAB实现变分贝叶斯蒙特卡洛模拟的贝叶斯推断 效果一览 基本介绍 研究内容 模型描述 模型设计 参考资料 效果一览 基本介绍 MATLAB实现变分贝叶斯蒙特
  • input输入框获取焦点之后,显示搜索记录下拉表,点击其他地方搜索记录框消失

    给input框绑定一个focus事件 获取焦点时给全局绑定一个点击事件 判断下次点击的地方在不在输入框和下拉框内 不在则下拉框消失 记得清除这个点击事件 具体代码如下 处理搜索框获取焦点 handleInputSearch this isO
  • elementui中多个table同步滚动

    问题描述 element admin中同时使用多个table 要求头部固定 给每个并列的table设置max height 通过监听一侧的table滚动情况去控制另外一侧的table滚动 问题分析 table分成两部分 左侧是一个table
  • 用python画基本初等函数的图像(未完成)

    要用到matplotlib库 一 绘制 y x 图像 import numpy as np import matplotlib pyplot as plt x np linspace 50 50 200 定义x的范围为 50到50 分为20
  • UVA-11059 最大乘积 题解答案代码 算法竞赛入门经典第二版

    GitHub jzplp aoapc UVA Answer 算法竞赛入门经典 例题和习题答案 刘汝佳 第二版 数据量不大 暴力即可 include
  • 【AI】《动手学-深度学习-PyTorch版》笔记(二十):图像增强、微调

    AI学习目录汇总 1 图像增强 图像增强可以扩展训练样本数量 减小对某个属性的依赖 比如 裁剪图像 可以减少模型对对象出现位置的依赖 调整亮度 颜色等因素来降低模型对颜色的敏感度等 1 1 准备工作 头文件 matplotlib inlin
  • 01C++11多线程编程之thread,join,detach,joinable以及简说detach传引用地址的大坑

    01C 11多线程编程之thread join detach joinable以及简说detach传引用地址的大坑 1 thread类对象创建线程与join回收线程 1 thread创建线程很简单 定义一个对象然后传一个可调用对象即可 可调
  • Java反射机制【看这一篇就够啦!!!】

    Welcome Huihui s Code World 接下来看看由辉辉所写的关于反射机制的相关操作吧 目录 Welcome Huihui s Code World 一 是什么 二 为什么要使用 三 怎么使用 辉辉小贴士 什么是Class类
  • 数字电路设计之同步电路的一些经验

    在设计的过程中 异步复位电路对硬件要求更低 更容易实现 但是使用同步复位电路却有着诸多优点 使得在实际的工业设计中更多使用的是同步复位电路 使用同步电路一般有以下好处 第一个就是避免毛刺 使用逻辑电路就一定会有毛刺 使用同步电路就有效避免毛
  • Vue生成二维码组件封装

    1 使用方法 1 1 载入 JavaScript 文件 1 2 调用 简单方式 new QRCode document getElementById qrcode your content 设置参数方式 var qrcode new QRC
  • Ubuntu彻底卸载pycharm的方法

    1 查看配置信息位置 首先在解压的pycharm 2020 2 1文件夹中 查看Install Linux tar txt 找到配置信息的位置 下图中蓝色标识 2 卸载安装文件 首先找到安装文件所在的目录 cd 切换至其目录 然后 sudo
  • 深入了解React:组件化开发与状态管理

    简介 React是一个流行的JavaScript库 用于构建用户界面 它以其高效的虚拟DOM和组件化开发的思想而闻名 使得构建复杂的Web应用程序变得更加简单和可维护 本篇博客将带您深入了解React的基本概念 组件化开发和状态管理 帮助您
  • FISCO BCOS(三十六)———Python Sdk window环境部署及应用开发

    1 环境要求 Python环境 python 3 6 3 3 7 3 最好是3 7 3 因为我是 FISCO BCOS节点 可以直接建链 可以是节点前置 也可以是一键部署 2 安装python 2 1 python下载地址 https ww
  • COM 组件设计与应用(十一)

    COM 组件设计与应用 十一 IDispatch 及双接口的调用作者 杨老师 下载源代码一 前言 前段时间 由于工作比较忙 没有能及时地写作 其间收到了很多网友的来信询问和鼓励 在此一并表示感谢 咳 我也需要工作来养家糊口呀 上回书介绍了两