




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、126,十三. 自动化对象,概念澄清: 类型库与自动化接口Idispatch完全独立 自动化对象与自动化控制完全独立 命名的混乱 类型库 IDispatch接口 自动化对象的实现 使用自动化对象客户 晚绑定 DISPID绑定 早绑定、 自动化对象编程实践,226,1类型库,COM不仅追求C+编译器的中立,而且追求语言的独立性. 因此它使用IDL语言来描述接口. 然后在IDL到具体的语言之间建立映射. 但是一些数据类型在有些语言中难以表达。比如复杂的结构类型,指针类型,函数指针等等在一些弱类型的高级语言中比如Java, Visual Basic等等并没有得到支持. IDL到这些语言的映射不能顺利
2、地进行. 客户通过接口调用对象时,在编译时刻需要接口的准确的描述, 这个描述正是来自于MIDL对IDL编译后产生的头文件, 而Java, VB等无法使用这种基于C/C+的头文件. COM的语言无关性受到很多的限制。 因此, MS使用类型库来解决这个问题. 类型库文件是一个二进制文件, 后缀为.tlb.用MIDL工具编译idl文件可以产生类型库文件,在实际的开发过程中不一定要手工使用MIDL工具,IDE对其进行了集成. 编译完成以后,我们可以选择把它随组件库一起分发. 类型库以机器可读的方式描述了组件与外界交互的必要信息. 如COM对象的CLSID, 它支持的接口的IID,接口的成员函数的签名等
3、等. 本质上它等价于描述接口的C/C+头文件.,326,一个类型库可以包含多个COM对象,这些COM对象可以实现多个接口,而且一般而言实现了IDispatch接口(不是必须).为了标识这些类型库,也使用GUID来作为它的唯一标识LIBID.并且也在注册表中注册,注册位置是HKEY-CLASSES_ROOTTypeLib,注册内容主要指明类型库所描述的对象的载体(dll文件等)的位置. VB, Java等语言的开发者不需要直接面对类型库. 相反,它是由编译器环境(VB虚拟机,Java虚拟机)来解释它. 这样它使得开发者在开发期能够浏览接口的相关信息. (以VB为例,通过Reference添加对类
4、型库的引用后,使用Object Browser就可以查看COM接口了, 另一个工具OLE/COM Object Viewer使用更加方便). 而开发人员只需要使用宿主语言简单的语法,非常方便地使用COM. (烦心事交给编译器的开发者去吧! 我们看到,如果不是使用COM,而是以一般的库函数的形式,在VB这样的高端应用中使用起来就没有这么简便(对最终开发者而言). 每一样复杂的技术,在使用者的舒适的背后,是底层开发者的艰辛) 当然,如果愿意,C+编译器也可以利用类型库. Visual CIDE中的ClassWizard和CBuilderIDE,DElphi中的importType Library命
5、令都可以读入组件的类型库,并利用其中的信息产生C代码。客户程序利用这些代码可以使用COM组件。,426,并不是只有IDE的开发者才知道怎样解析类型库. 为了操作类型库,Windows提供了一些API(LoadTypeLib 和LoadRegTypeLib等)和COM接口(ITypeLib和ITypeInfo等). LoadTypeLib可以根据指定的文件名装载类型库,并返回ITypeLib接口. 使用LoadRegTypeLib可以根据类型库的LIBID查找注册表,找到类型库文件,返回ITypeLib接口. ITypeLib接口代表了类型库本身.使用其GetTypeInfoofGuid根据接口
6、的IID或者使用GetTypeInfo根据接口在类型库中的索引号可以返回ITypeInfo接口. ITypeInfo接口则代表了接口的全部信息.包括有哪些方法,方法的签名等等. 如果接口是IDispatch接口,则还可以使用GetIDsofNames函数来根据方法的名字得到其分发ID,并使用Invoke函数通过方法的分发ID来执行这个方法. 因此,为了在编译时刻了解接口的信息, 客户程序要么得到COM组件的IDL文件(使用头类型定义头文件,在代码中通知编译器接口的类型,如C+), 要么得到它的类型库文件(代码中没有准确的信息,由IDE环境从类型库中读取接口类型信息,如VB), 才能顺利地构造客
7、户应用程序,从而使用COM对象.,526,无论是通过头文件,还是通过类型库,我们在开发客户程序时都有关于接口的先验知识.这些先验信息帮助我们顺利地编译客户程序.这种方式我们有时称为静态调用,或者早绑定(early binding). 但是,还存在这样的情况,有的语言在开发过程中并没有经过编译阶段,而是直接以源代码的形式被配置发布. 在运行时才被解释运行.比如以HTML为基础的脚本语言.(VBScript,JavaScript等).它们在浏览器或Web服务器的环境中执行. 脚本代码以纯文本的形式嵌入在HTML文件中. 为了丰富脚本的功能,它们也可以创建COM对象,执行特殊的功能,比如访问数据库等
8、等. 比如: var obj = new ActiveXObject(“LuBenjie.AutoObj); alert(obj.Hello(); 在脚本引擎中,目前还不能使用类型库或其他的先验知识来描述接口的信息.这意味着对象自身要帮助脚本解释器,将文本形式的脚本代码翻译为有意义的方法调用. 这种方式我们称为动态调用,或者晚绑定(late binding). 为了支持晚绑定,COM定义了一个接口,用来表达这种翻译机制,这个接口就是IDispatch.分发接口有时称为自动化接口,实现了此接口的对象称为自动化对象. 自动化接口的定义如下:,2 IDispatch接口,626,class IDis
9、patch:public IUnknown public: HRESULT GetTypeInfoCount( unsigned int FAR* pctinfo ); /如果对象提供类型支持,则返回1,否则0. 客户在获取类型信息之前先使用此函数进行判断. HRESULT GetTypeInfo( unsigned int iTInfo, LCID lcid, ITypeInfo FAR* FAR* ppTInfo ); / 一般给iTInfo赋值0, 返回指向对象类型信息的ITypeInfo接口指针, 通过ITypeInfo接口可以访问该自动化接口的所有类型信息. HRESULT GetI
10、DsOfNames( REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgDispId ); / 返回指定名字的方法或属性的分发ID. IDispatch使用分发ID管理接口的属性和方法. rgszNames 指定属性或方法的名字, rgDispId返回其分发ID HRESULT Invoke( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR* pDispParams, VA
11、RIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo, unsigned int FAR* puArgErr ); ; /是命令的翻译器。客户程序通过invoke函数访问方法或属性。客户给定分发ID dispIdMember 、输入参数pDispParams 。invoke返回输出参数pDispParams .自动化对象所有的方法和属性的调用都通过invoke函数来 实现。使得运行时刻动态绑定属性和方法并进行参数类型检查成为可能.,726,当一个脚本引擎首次尝试访问一个对象时,它使用QueryInterface向对象请求IDispatch接口.如果请
12、求失败,则不能使用此对象.如果成功,则继续调用GetIDsofName方法,得到方法或属性的分发ID号.通过此ID号,调用Invoke方法,就可以调用想要调用的方法. 分发接口与普通接口的区别在于,接口的逻辑功能是如何被调用的.普通的COM接口是以该方法的静态的先验知识为基础.而分发接口是以该方法的预期的文字表示为基础.如果调用者正确地猜测出方法的原型,那么此调用可以被顺利地分发,否则不能. 假设有一个自动化对象CMath,它只实现了分发接口,进行加减乘除的工作.这些具体的工作由内部函数来完成.并没有向外界提供接口.这些计算功能由Invoke函数根据分发ID来调用特定的函数. uuid(C28
13、95C1F-020E-4C1F-8A65-F59094DFBD97) dispinterface DMath /dispinterface 关键字说明这是一个分发接口. properties: methods: id(0) long Add(long Op1,long Op2); /0,1,2,3分别是分发ID id(1) long Substract(long Op1,long Op2); id(2) long Multiply(long Op1,long Op2); id(3) long Divide(long Op1,long Op2); 此对象的虚表及其分发表示意图如下:,826,自动
14、化对象的虚表和分发表.,926,自动化对象可以只实现分发接口: class CMath:public IDispatch public: /来自IUnknown的三个函数 virtual HRESULT _stdcall QueryInterface() ; virtual ULONG_stdcall AddRef() ; virtual ULONG_stdcall Release() ; / 来自IDispatch的四个函数 HRESULT GetTypeInfoCount( ); HRESULT GetTypeInfo( ); HRESULT GetIDsOfNames(); HRESUL
15、T Invoke( ); ;/此COM对象只能通过分发接口给外界提供服务.虽然这样做显得别扭,有舍近求远之嫌, 但是,原理上是可行的.,1026,更常用地,我们把具体的计算功能也作为接口直接暴露出去,我们从IDispatch派生一个接口IMath. object, uuid(2756E11C-A606-482F-969C-14153E1D1609), dual/说明是一个双接口 interface IMath: IDispatch properties: methods: id(0) HRESULT Add /0,1,2,3分别是分发ID (in long Op1,in long Op2,ou
16、t,retval long* pResult); id(1) HRESULT Substract (in long Op1,in long Op2,out,retval long* pResult); id(2) HRESULT Multiply (in long Op1,in long Op2,out,retval long* pResult); id(3) HRESULT Divide (in long Op1,in long Op2,out,retval long* pResult); 自动化对象实现双接口:,1126,class CMath:public IMath public: /
17、来自IUnknown的三个函数 virtual HRESULT _stdcall QueryInterface() ; virtual ULONG_stdcall AddRef() ; virtual ULONG_stdcall Release() ; / 来自IDispatch的四个函数 HRESULT GetTypeInfoCount( ); HRESULT GetTypeInfo( ); HRESULT GetIDsOfNames(); HRESULT Invoke( ); / 来自IMath的三个函数 HRESULT Add(long Op1, long Op2, long* pRes
18、ult); HRESULT Substract(long Op1, long Op2, long* pResult); HRESULT Multiply(long Op1, long Op2, long* pResult); HRESULT Divide(long Op1, long Op2, long* pResult); ;/此COM对象同时通过分发接口给外界提供分发调用服务;通过IMath接口直接通过虚表来提供普通的服务. 实现双接口的自动化对象的虚表和分发表:,1226,实现双接口的自动化对象的虚表和分发表,1326,3 自动化接口的实现,分发接口的四个函数从功能上来说分为两组: Ge
19、tTypeInfoCount与GetTypeInfo函数表示对类型库的支持. 通常客户并不需要从分发接口的这两个函数中来访问类型库.如果愿意,客户可以借助IDE生成封装类,或者直接使用操作类型库也可以. 但如果真要实现它,那么: 提供类型库文件 (MIDL编译器对IDL编译的结果) GetTypeInfoCount返回1, 否则返回0; GetTypeInfo 使用LoadTypeLib得到ITypeLib接口.然后得到 ITypeInfo接口.一旦客户得到ITypeInfo接口指针就可以完全地了解接口的类型及其所支持的属性和方法。 GetIDsOfNames和Invoke完成函数的分发调用.
20、 GetIDsOfNames有两种实现方法: 1.由自动化对象自己实现。它当然知道自己所有的方法和属性的分发ID。使用switch case或者如果数目太多的话,使用表格进行查表.,1426,HRESULT GetIDsOfNames( REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgDispId ) / 假设cNames=1,即一回只查一个名字. char * str=OLE2T(rgszzNames0); if (strcmp(“Add”,str,3)=0) rg
21、DispId0=0; /加法返回0 else if (strcmp(“Substract”,str,8)=0) rgDispId0=1; /减法返回1 else if 2.如果实现了GetTypeInfo,那么直接从其中得到ITypeInfo指针,然后使用这个指针的GetIDsOfNames方法即可.(绕了一大圈,但是也可行). HRESULT GetIDsOfNames() ITypeInfo * pITI; GetTypeInfo( ,1526,Invoke函数的实现。 1。可以根据分发ID,逐个分支处理,可以使用内部函数,或者,如果是双接口,分支内部直接使用IMath接口的功能函数. H
22、RESULT Invoke( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR* pDispParams, VARIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo, unsigned int FAR* puArgErr ); switch (dispIdMember) case 0: /作加法, 直接实现,或者调用内部函数. case1: /减法 2。使用类型信息指针。 如果实现了GetTypeInfo,那么直接从其中得到ITypeInfo指针,然后使用
23、这个指针的Invoke方法即可.(也绕了一大圈,但是也可行). HRESULT Invoke() ITypeInfo * pITI; GetTypeInfo( ,1626,4.使用自动化对象.,对于自动化对象的使用,根据其实现接口和对类型库的支持程度不同, 有不同的使用方法: 只实现了分发接口,没有提供类型库.只能使用晚绑定. 实现了分发接口,提供了类型库,当然可以使用晚绑定,也可以使用DISPID绑定(早绑定的一种,为了区分起见就命名为DISPID绑定). 实现了双接口,提供了类型库, 那么可以使用晚绑定,DISPID绑定和早绑定. 晚绑定-DISPID绑定-早绑定 性能越来越高. 灵活性越
24、来越低.,1726,4.1 晚绑定,晚绑定. 一般的COM对象都只能使用早绑定.但是自动化对象可以使用晚绑定.是重要特色之一.开发阶段不进行类型检查,运行时决定组件的功能. 代价昂贵,速度最慢. 灵活性最高. 服务器接口发生变化(比如说分发ID变了) ,客户程序不用重现编译. 1.使用VB Dim obj as Object Set obj=CreateObject(“MathLib.Math”) /动态创建. obj.Add(10,20) /结果为30 Set obj=Nothing /释放对象 注意,在编译时刻并没有进行类型检查, obj.Add(10,20) 纯属猜测! 如果方法的名字不
25、符合或者参数不符合,都将引起运行时错误. 2.使用C+. 使用C+ 我们能更清楚地看到分发调用的过程.(虽然晚绑定一般是针对VB这样的语言的),1826,首先看函数调用调用的参数类型 typedef struct tagDISPPARAMS VARIANTARG *rgvarg; /参数数组,类型为VARIANT,大小为cArgs DISPID *rgdispidNamedArgs;/命名参数的ID数组. UINT cArgs; /参数的个数 UINT cNamedArgs; /命名参数的个数 见MSDN文档 DISPPARAMS; 其中VARIANT是一个结构体,结构体中包含巨大的Union
26、和一个指示实际类型的域vt.见MSDN文档. 在使用晚绑定时,只能使用VARIANT所支持的数据类型. 客户的调用代码: IDispatch *pD; HRESULT hr=CoCreateInstance(CLSID_Math, NULL, CLSCTX_SERVER, IID_IDispatch, /加法字符串对应的分发ID存在此, 下面先找到它,1926,pD-GetIDsofNames(IID_NULL, lpOleStr, 1,LOCAL_SYSTEM_DEFAULT, /释放接口,2026,注意以上计算过程,我们只是使用了分发接口,我们猜测了加法的名字和参数.我们事先没有使用到自动
27、化对象的任何信息.不需要包含接口声明的头文件. 编译时刻没有进行任何类型检查. 如果猜测失误将引起运行时错误.,2126,4.2 DISPID绑定,如果提供类型库,那么就可以在编译时进行类型检查. VB中使用Reference导入类型库.我们就可以象VB中固有的数据类型一样使用COM对象.编译器将根据组件中的类型信息检查代码中的语法和参数类型. VB为方法和属性缓存一个DISPID. 避免在运行时刻去查询方法或属性的分发ID. 以上措施,可以避免出错,提高性能. 组件的接口改变时,要重新编译客户程序. Dim obj as New MathLib.Math obj.Add(10,20) /返回
28、30 不是猜测的! 如果不符合,则编译会出错! 这是类型库起的作用. 下面看C+中如何使用DISPID绑定 MFC提供了COleDispatchDriver类,可以用来使用DISPID绑定来访问自动化对象:,2226,COleDispatchDriver类是MFC提供的封装类,它通过自动化对象的类型库把原自动化对象的方法和属性的分发ID硬性地记录下来, 把原来的方法和属性在封装类中进行封装. 使得用户避免复杂的invoke参数序列, COleDispatchDriver 有一个数据成员m_lpDispatch,它包含了对应组件的IDispatch接口指针。COleDispatchDriver提
29、供了几个成员函数包括InvokeHelper GetProperty SetProperty,这三个函数通过m_lpDispatch调用invoke函数。 COleDispatchDriver的其他成员管理IDispatch接口指针,CreateDispatch根据CLSID创建自动化对象,并把IDispatch接口指针赋给m_lpDispatch成员。AttachDispatch使得当前的COleDispatchDriver与某个自动化对象联系起来。DetachDispatch则取消这种联系。,2326,两种使用方式: 根据组件的类型库生成COleDispatchDriver的派生类。从Class
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 培训机构住宿管理办法
- 现代教育技术支持下的教学模式创新研究
- 杭州概算控制管理办法
- 粉末冶金钛合金快速烧结过程研究:微观组织变化及其机制
- 社区治理中的“老有所为”与积极老龄化路径探索
- 园区低频噪音管理办法
- “数实融合”在皮革行业高质量发展中的作用研究
- 公务接待超市管理办法
- 干湿和氧化条件下生物炭对溶液中Cd2吸附机制的研究
- 农垦食品安全管理办法
- 办公室副主任考试试题及答案详解
- 《电火花检漏仪校准规范试验报告》
- 克拉玛依市公安局招聘警务辅助人员考试真题2024
- 供应蒸汽服务合同协议书
- 中国机器人工程市场调研报告2025
- 2025年金融科技企业估值方法与投资策略在金融科技企业并购中的应用案例报告
- 《无人机介绍》课件
- 2025-2030中国硼酸行业市场发展现状及竞争格局与投资研究报告
- 学校中层干部选拔聘用实施方案中层干部选聘实施方案2
- 生物必修1教师用书
- 园艺植物育种学知到课后答案智慧树章节测试答案2025年春浙江大学
评论
0/150
提交评论