COM 组件设计与应用(十一)—— IDispatch 及双接口的调用
一、前言 前段时间,由于工作比较忙,没有能及时地写作。其间收到了很多网友的来信询问和鼓励,在此一并表示感谢。咳......我也需要工作来养家糊口呀...... 上回书介绍了两种方法来写自动化(IDispatch)接口的组件程序,一是用 MFC 方式编写“纯粹”的IDispatch 接口;二是用 ATL 方式编写“双接口”的组件。 二、IDispatch 接口和双接口 使用者要想调用普通的 COM 组件功能,必须要加载这个组件的类型库(Type library)文件 tlb(比如在 VC 中使用 #import)。然而,在脚本程序中,由于脚本是被解释执行的,所以无法使用加载类型库的方式进行预编译。那么脚本解释器如何使用 COM 组件那?这就是自动化(IDispatch)组件大显身手的地方了。IDispatch 接口需要实现4个函数,调用者只通过这4个函数,就能实现调用自动化组件中所有的函数。这4个函数功能如下:
从 Invoke() 函数的实现就可以看出,使用 IDispatch 接口的程序,其执行效率是比较低的。ATL 从效率出发,实现了一种叫“双接口(dual)”的接口模式。下面我们来看看,到底什么是双接口: 图一、双接口(dual) 结构示意图 从上图中可以看出,所谓双接口,其实是在一个 VTAB 的虚函数表中容纳了三个接口(因为任何接口都是从IUnknown 派生的,所以就不强调 IUnknown 了,叫做双接口)。我们如果从任意一个接口中调用QueryInterface()得到另外的接口指针的话,其实,得到的指针地址都是同一个。双接口有什么好处那?答:好呀,多好呀,特别好呀......
三、使用方法 如果你的开发环境是 vc6.0,那么我们使用第九回中的Simple6组件为例,快去下载呀...... 如果你的开发环境是 vc.net 2003,那么用第十回中的Simple8组件为例,快去下载呀...... 嘿嘿,其实不下载也没有关系,因为你只要下载本回的示例程序,里面已经包含了所需的组件。但使用前不要忘了去注册呀:regsvr32.exe simple6.dll 或 regsvr32.exe simple8.dll (注意别忘了输入组件的安装目录)。注册成功后,就可以使用了,使用方法有:
示例一、IDispatch 调用原理篇 01. void demo()
02. {
03. ::CoInitialize( NULL ); // COM 初始化
04.
05. CLSID clsid; // 通过 ProgID 得到 CLSID
06. HRESULT hr = ::CLSIDFromProgID( L "Simple8.DispSimple.1" , &clsid );
07. ASSERT( SUCCEEDED( hr ) ); // 如果失败,说明没有注册组件
08.
09. IDispatch * pDisp = NULL; // 由 CLSID 启动组件,并得到 IDispatch 指针
10. hr = ::CoCreateInstance( clsid, NULL, CLSCTX_ALL, IID_IDispatch, ( LPVOID *)&pDisp );
11. ASSERT( SUCCEEDED( hr ) ); // 如果失败,说明没有初始化 COM
12.
13. LPOLESTR pwFunName = L "Add" ; // 准备取得 Add 函数的序号 DispID
14. DISPID dispID; // 取得的序号,准备保存到这里
15. hr = pDisp->GetIDsOfNames( // 根据函数名,取得序号的函数
16. IID_NULL,
17. &pwFunName, // 函数名称的数组
18. 1, // 函数名称数组中的元素个数
19. LOCALE_SYSTEM_DEFAULT, // 使用系统默认的语言环境
20. &dispID ); // 返回值
21. ASSERT( SUCCEEDED( hr ) ); // 如果失败,说明组件根本就没有 ADD 函数
22.
23. VARIANTARG v[2]; // 调用 Add(1,2) 函数所需要的参数
24. v[0].vt = VT_I4; v[0].lVal = 2; // 第二个参数,整数2
25. v[1].vt = VT_I4; v[1].lVal = 1; // 第一个参数,整数1
26.
27. DISPPARAMS dispParams = { v, NULL, 2, 0 }; // 把参数包装在这个结构中
28. VARIANT vResult; // 函数返回的计算结果
29.
30. hr = pDisp->Invoke( // 调用函数
31. dispID, // 函数由 dispID 指定
32. IID_NULL,
33. LOCALE_SYSTEM_DEFAULT, // 使用系统默认的语言环境
34. DISPATCH_METHOD, // 调用的是方法,不是属性
35. &dispParams, // 参数
36. &vResult, // 返回值
37. NULL, // 不考虑异常处理
38. NULL); // 不考虑错误处理
39. ASSERT( SUCCEEDED( hr ) ); // 如果失败,说明参数传递错误
40.
41. CString str; // 显示一下结果
42. str.Format( "1 + 2 = %d" , vResult.lVal );
43. AfxMessageBox( str );
44.
45. pDisp->Release(); // 释放接口指针
46. ::CoUninitialize(); // 释放 COM
47. }
示例二、CComDispatchDriver 智能指针包装类的使用方法 01. void demo()
02. {
03. // 已经进行过了 COM 初始化
04.
05. CLSID clsid; // 通过 ProgID 取得组件的 CLSID
06. HRESULT hr = ::CLSIDFromProgID( L "Simple8.DispSimple.1" , &clsid );
07. ASSERT( SUCCEEDED( hr ) ); // 如果失败,说明没有注册组件
08.
09. CComPtr < IUnknown > spUnk; // 由 CLSID 启动组件,并取得 IUnknown 指针
10. hr = ::CoCreateInstance( clsid, NULL, CLSCTX_ALL, IID_IUnknown, ( LPVOID *)&spUnk );
11. ASSERT( SUCCEEDED( hr ) );
12.
13. CComDispatchDriver spDisp( spUnk ); // 构造只能指针
14. CComVariant v1(1), v2(2), vResult; // 参数
15. hr = spDisp.Invoke2( // 调用2个参数的函数
16. L "Add" , // 函数名是 Add
17. &v1, // 第一个参数,值为整数1
18. &v2, // 第二个参数,值为整数2
19. &vResult); // 返回值
20. ASSERT( SUCCEEDED( hr ) ); // 如果失败,说明或者没有 ADD 函数,或者参数错误
21.
22. CString str; // 显示一下结果
23. str.Format( "1 + 2 = %d" , vResult.lVal );
24. AfxMessageBox( str );
25. }
示例程序中使用了 Invoke2()函数,其实你根据不同的函数,还可以使用Invoke0()、Invoke1()、InvokeN()、PutProperty()、GetProperty()......等等等,的确很方便。 示例三、加载类型库,产生包装类来使用 这个方法使用更简单一些,如果你观察 MFC 帮你产生的包装类的实现,你就会发现,其实它调用的是IDispatch 接口函数。使用 vc6.0 的朋友,步骤如下: 1、建立一个 MFC 的应用程序 2、开启 ClassWizard,执行 Add Class,选择 From a type library 图二、加载类型库 3、然后找到你要使用的组件文件 simple6.dll(tlb 文件也可以),选择接口后确认 图三、选择类型库中需要包装的接口 4、在适当的地方输入调用代码 01. #include "simple6.h" // 包装类的头文件
02.
03. void demo()
04. {
05. // 已经进行过了 COM 初始化
06.
07. IDispSimple spDisp; // 包装类的对象
08.
09. spDisp.CreateDispatch( _T( "Simple6.DispSimple.1" ) ) //启动组件
10. spDisp.xxx(...); // 调用函数
11.
12. spDisp.ReleaseDispatch(); // 释放接口
13. }
使用 vc.net 的朋友,步骤如下: 1、建立一个 MFC 的应用程序 2、执行菜单“添加\添加类”,选择 MFC 分类中的“类型库中的MFC类” 图四、添加类型库中的MFC类 3、选择组件文件 simple8.dll(或 tlb 文件),并选择需要包装的接口 图五、选择文件和接口 4、在适当的位置输入调用代码 01. #include "CDispSimple.h" // 包装类的头文件
02.
03. void demo()
04. {
05. // 已经进行过了 COM 初始化
06.
07. CDispSimple spDisp; // 包装类的对象
08. spDisp.CreateDispatch( _T( "Simple8.DispSimple.1" ) ) // 启动组件
09. spDisp.xxx(...); // 调用函数
10.
11. spDisp.ReleaseDispatch(); // 释放接口
12. }
示例四、使用 #import 方式调用组件 #import 方式在第七回中已经作过介绍,这里就不多罗嗦了。大家下载本回的示例程序后,自己去看吧。并且一定要掌握这个方法,因为它的运行效率是最快的呀。 四、小结 留作业啦。在我们以前所实现的所有组件程序中,只添加了接口方法(函数),而没有添加接口属性(变量),你自己练习一下吧,很简单的,然后写个程序调用看看。其实对于 VC 来说,调用属性和调用方法没有太大的区别(vc 把属性包装为 GetXXX()/PutXXX()或getXXX()/putXXX()的函数方式),但在另外一些语言中(比如脚本语言)则更方便,设置属性值是:对象.属性 = 变量或常量,获取属性值是:变量 = 对象.属性。 本回书至此做一了断,更多组件设计和使用的知识,且听下回分解...... 注1:多个自动化接口的实现方法,我们以后再说。 注2:将来介绍 ITypeLib::GetTypeInfo() 的时候,大家再回味 IDispatch::GetTypeInfo()吧。 注3:在后面介绍“事件”的时候,我们会自己真正去实现一个 IDispatch::Invoke() 函数。 注4:介绍多个双接口实现的时候,会谈到这个问题。 |
COM 组件设计与应用(十一)—— IDispatch 及双接口的调用相关推荐
- COM 组件设计与应用(十一)
COM 组件设计与应用(十一) IDispatch 及双接口的调用 作者:杨老师 下载源代码 一.前言 前段时间,由于工作比较忙,没有能及时地写作.其间收到了很多网友的来信询问和鼓励,在此一并 ...
- COM 组件设计与应用
目录 COM 组件设计与应用(一) 起源及复合文件... 1 COM组件设计与应用(二) GUID 和 接口... 10 COM组件设计与应用(三) 数据类型... 20 COM组件设计与应用(四) ...
- COM 组件设计与应用(六)——用 ATL 写第一个组件(vc.net)
一.前言 1.与 <COM 组件设计与应用(五)>的内容基本一致.但本回讲解的是在 vc.net 2003 下的使用方法,即使你不再使用vc6.0,也请和上一回的内容,参照比对. 2.这第 ...
- COM 组件设计与应用(一)
COM 组件设计与应用(一) 起源及复合文件 作者:杨老师 一.前言 公元一九九五年某个夜黑风高的晚上,我的一位老师跟我说:"小杨呀,以后写程序就和搭积木一样啦.你赶快学习一些OLE的技术吧 ...
- COM组件设计与应用(十三)(转载)
COM组件设计与应用(十三) 事件和通知(VC6.0) 作者:杨老师 下载源代码 一.前言 我的 COM 组件运行时产生一个窗口,当用户双击该窗口的时候,我需要通知调用者: 我的 COM 组件用线程方 ...
- COM 组件设计与应用(七)
COM 组件设计与应用(七) 编译.注册.调用 作者:杨老师 一.前言 上两回中,咱们用 ATL 写了第一个 COM 组件程序,这回中,主要介绍编译.注册和调用方法.示例程序你已经下载了吗?如果还没有 ...
- 【转载】COM 组件设计与应用(二)——GUID 和 接口
原文:http://vckbase.com/index.php/wv/1203.html COM 组件设计与应用 系列文章:http://vckbase.com/index.php/piwz?& ...
- ueditor上传组件显示乱码_最全面的移动端 UI组件设计详解:中篇
上一期给大家讲解了<最全面的移动端UI组件设计详解:上篇>,主要分享了:布局组件和导航组件2个部分:这次给大家带来:基础组件.表单组件和反馈组件详解,希望你在设计APP.小程序.H5页面中 ...
- vue 新手指引_精通react/vue组件设计之快速实现一个可定制的进度条组件
前言 这篇文章是笔者写组件设计的第四篇文章,之所以会写组件设计相关的文章,是因为作为一名前端优秀的前端工程师,面对各种繁琐而重复的工作,我们不应该按部就班的去"辛勤劳动",而是要根 ...
最新文章
- 亚洲杯:打平韩国即可小组第一 国足会继续带来惊喜吗?
- 天翼云从业认证(4.9)工业企业上云解决方案
- kibana安装与Kibana server is not ready yet
- golang select default continue_golang系列——基础语法
- VC皮肤库SkinSharp 1 0 6 6的使用
- java8 内存设置_Java 8内存分析
- 死亡搁浅运送系统服务器,死亡搁浅订单23寻物系统服务器流程介绍-死亡搁浅订单23寻物系统服务器怎么做_牛游戏网...
- 红包不是你想送就能送 摩拜物联网技术成行业壁垒
- sqlite3 语法
- 软件工程第二次作业——个人项目
- [算法笔试题]华为相关复习题(更新中)
- Unity Shader 绘制朱利亚集合 Julia 奇幻图形
- 重庆市企业数据名录爬取采集-信用中国(重庆)
- Win10安装fliqlo时钟屏保教程
- 我国古代数学家张丘建在《算经》一书中曾提出过著名的“百钱买百鸡”问题,该问题叙述如下: 鸡翁一,值钱五;鸡母一,值钱三;鸡雏三,值钱一;百钱买百鸡,则翁、母、雏各几何?
- Word文档段落的前后间距单位磅改为行,行改为磅方法演示
- contiki学习心路历程
- 【Centos7 NTP 服务器和客户端配置(含离线状态) 附赠手动配置系统时间】
- 清爽即正义,简洁即真理—lingvist
- 数据概览神器—Pandas-profiling
热门文章
- python grid用法_Python numpy.mgrid函数方法的使用
- SSM框架中分页插件pageHelper的使用实例
- 学习笔记:文本过滤_____unix 下的通配符
- php mysql db封装类_封装自己的DB类(PHP)
- 鸿蒙os2.0通知栏,网友上手鸿蒙手机OS 2.0公测版:界面与EMUI已有明显不同
- 基于 Istio 的全链路灰度方案探索和实践
- Serverless Kubernetes 入门:对 Kubernetes 做减法
- mysql ef 分布式事务_分布式事务系列--分布式跨库查询解决方案 mysql federated引擎的使用...
- dell系统重装后无法进入系统_笔记本电脑常见故障开机无法进入系统
- java web.xml_Java Web之XML基础