COM组件 IDispatch 及双接口的调用
转自:http://blog.csdn.net/cnhk1225/article/details/50555647
一、前言
前段时间,由于工作比较忙,没有能及时地写作。其间收到了很多网友的来信询问和鼓励,在此一并表示感谢。咳......我也需要工作来养家糊口呀......
上回书介绍了两种方法来写自动化(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)”的接口模式。下面我们来看看,到底什么是双接口:
图一、双接口(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 vb、java、c#、bcb、delphi....... 反正我不会,自己去请教高人去吧
COM组件 IDispatch 及双接口的调用的更多相关文章
- 【转载】COM 组件设计与应用(十一)—— IDispatch 及双接口的调用
原文:http://vckbase.com/index.php/wv/1236.html 一.前言 前段时间,由于工作比较忙,没有能及时地写作.其间收到了很多网友的来信询问和鼓励,在此一并表示感谢.咳 ...
- COM组件双接口对象模型
模型如下: 这里COM对象一共实现了三个接口,IUnknown,IDispatch, Ixxx. 每个COM都必须实现IUnknown,不考虑在内的话共实现了IDispatch和自定义接口Ixxx两个 ...
- Qt组件中的双缓冲无闪烁绘图
双缓冲绘图在Qt4中,所有的窗口部件默认都使用双缓冲进行绘图.使用双缓冲,可以减轻绘制的闪烁感.在有些情况下,用户要关闭双缓冲,自己管理绘图.下面的语句设置了窗口部件的Qt::WA_PaintOn ...
- XRPC之接口双向调用
一般远程接口调用的服务都是基于客户端主动调用服务端,由服务端来提供相关的接口服务:在新版本的XRPC中引入了一个新的功能,即接口双向通讯,组件提供服务创建客户会话的接口代理并调用客户提供的接口服务.接 ...
- 【转】java通用URL接口地址调用方式GET和POST方式
java通用URL接口地址调用方式GET和POST方式,包括建立请求和设置请求头部信息等等......... import java.io.ByteArrayOutputStream; import ...
- 如何让Java和C++接口互相调用:JNI使用指南
如何让Java和C++接口互相调用:JNI使用指南 转自:http://cn.cocos2d-x.org/article/index?type=cocos2d-x&url=/doc/cocos ...
- 如何记录selenium自动化测试过程中接口的调用信息
上一篇博客,我写了python自动化框架的一些知识和粗浅的看法,在上一篇中我也给自己提出一个需求:如果记录在测试过程中接口的调用情况?提出这个需求,我觉得是有意义的.你在测试过程中肯定会遇到一些莫名其 ...
- Xamarin开发笔记—WebView双项事件调用
1.Xamarin调用WebView: 原理:Xamarin.Forms WebView内置方法xx.Eval(..)可以调用到页面里面的js函数. WebView展示的代码如下: var htmlS ...
- 在Winform混合式框架中整合外部API接口的调用
在我们常规的业务处理中,一般内部处理的接口多数都是以数据库相关的,基于混合式开发的Winform开发框架,虽然在客户端调用的时候,一般选择也是基于Web API的调用,不过后端我们可能不仅仅是针对我们 ...
随机推荐
- webpack 基本使用
1. 创建webpack-test文件夹 2. npm初始化 3. 安装webpack 4. 使用webpack打包 hello.js 是需要打包的文件 hello.bundle.js 是打包完以后 ...
- sql 判断 数据库 表 字段 是否存在
select * From master.dbo.sysdatabases where name='数据库名'select * from sysobjects where id = object_id ...
- 在react-native中使用es7语法中的decorator装饰器
在react-native中默认使用decorator会红屏报错,需要安装一个babel插件: babel-plugin-transform-decorators-legacy 然后在根目录下的.ba ...
- 服务注册发现Eureka之一:Spring Cloud Eureka的服务注册与发现
Spring Cloud简介 Spring Cloud是一个基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用开发中的配置管理.服务发现.断路器.智能路由.微代理.控制总线.全局锁 ...
- 过度使用DBLINK做系统集成会带来的问题
过度使用DBLINK做系统集成会带来很多问题,问题主要由以下几点: 1. 大量消耗数据库资源: 本地系统每通过DBLINK链接远端系统一次,都会生成一个本地session,如本地session不退出或 ...
- centos7 防火墙一些相关设置 开机添加静态路由 特殊的方法
参考文献: https://access.redhat.com/documentation/zh-cn/red_hat_enterprise_linux/7/html/security_guide/s ...
- [转载]tornado.database添加PooledDB连接池功能
转载自 http://chenxiaoyu.org/2009/12/01/python-tornado-dbutil.html tornado.database模块简单包装了下对MySQL的操作,短小 ...
- Noip2011Mayan游戏
题目描述 Mayan puzzle是最近流行起来的一个游戏.游戏界面是一个 7 行5 列的棋盘,上面堆放着一些方块,方块不能悬空堆放,即方块必须放在最下面一行,或者放在其他方块之上.游戏通关是指在规定 ...
- javaScript中对象属性的访问
有两种方式访问对象属性,一个是点操作符(.),一种是中括号操作符([ ]). 当你知道属性的名称时,使用点操作符: var myObj = { prop1: "val1", pro ...
- Fork-Join分治编程介绍(一)
一.Fork-Join 框架介绍 1. 什么是 Fork-Join 分治编程框架 Fork/Join框架是Java7提供了的一个用于并行执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每 ...