第11章 调度接口与自动化
1、自动化是建立在COM基础上的。
2、一个自动化服务器实际上就是一个实现了IDispatch接口的COM组件。
3、一个自动化控制器则是一个通过IDispatch接口同自动化服务器进行通信的COM客户。
自动化控制器不会直接调用自动化服务器实现的那些函数,而是通过IDispatch接口中的成员函数实现对服务器中函数的间接调用。
4、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] LCID lcid, [out,size_is(cNames)] DISPID * rgDispId) | 根据函数名称取得函数序号,为调用 Invoke() 做准备。 所谓函数序号,大家去观察双接口 IDL 文件和 MFC 的 ODL 文件,每一个函数和属性都会有 [id(序号)....] 这样的描述。 |
HRESULT Invoke( [in] REFIID riid, [in] LCID lcid, [in] WORD wFlags, [in,out] DISPPARAMS * pDispParams, [out] VARIANT * pVarResult, [out] EXCEPINFO * pExcepInfo, [out] UINT * puArgErr) | 根据序号,执行函数。 case 1: .....; break; case 2: .....; break; .... } 其实,就是根据序号进行分支调用啦。(注3)
|
5、双重接口:让实现IDispatch::Ivoke的COM组件继承IDispatch而不是IUnknown。它使得通过Invoke能够访问的函数也能够直接通过vtbl访问到(C++程序员使用vtbl)。所以叫双重接口。
6、双接口(dual) 结构示意图:
7、VARIANT:函数内部动态判断参数类型,实际上是不同类型值的一个大联合体union。
8、双接口有什么好处
使用方式 | 因为 | 所以 |
脚本语言使用组件 | 解释器只认识 IDispatch 接口 | 可以调用,但执行效率最低 |
编译型语言使用组件 | 它认识 IDispatch 接口 | 可以调用,执行效率比较低 |
编译型语言使用组件 | 它装载类型库后,就认识了 Ixxx 接口 | 可以直接调用 Ixxx 函数,效率最高啦 |
结论 | 双接口,既满足脚本语言的使用方便性,又满足编译型语言的使用高效性。 于是,我们写的所有的 COM 组件接口,都用双接口实现吗? 错!否!NO! 如果不是明确非要支持脚本的调用,则最好不要使用双接口,因为: | |
如果所有函数都放在一个双接口中,那么层次、结构、分类不清 | ||
如果使用多个双接口,则会产生其它问题(注4) | ||
双接口、IDispatch接口只支持自动化的参数类型,使用受到限制,某些情况下很不方便喽 | ||
还有很多弊病呦,不过现在我想不起来喽...... |
8、类型库:一种与语言无关的、适合解释性语言和宏编程语言使用的C++头文件的等价物。类型库将提供有关组件、接口、方法、属性、参数及结构的类型信息。类型库中的内容同C++头文件中的内容是相同的,他实际上是IDL文件的一个编译版本,并且可以用编程的方法来访问。类型库不是一个与语言无关的 需要被分析的文本文件,而是一个二进制文件。自动化库为创建和读取此二进制文件提供了一些标准的组件。
8、本章直接用随书提供的源码里的程序跑了一下,压根跑不起来。网上搜了搜,原来是因为注册类不支持汉字。移到没有汉字的路径下,就可以了。VC的工程源码在我的优快云资源中可下载InsideCOM\CHAP11XXX。另外还有两种方式可以实现双接口:MFC和ATL。