二、IDispatch接口
如果是编译型语言,那么我们可以让编译器在编译的时候装载类型库,也就是装载接口的描述。在第七回文章当中,我们分别使用了 #include 方法和 #import 方法来实现的。装载了类型库后,编译器就知道应该如何编译接口函数的调用了---这叫“前绑定”。但是,如果想在脚本语言中使用组件,问题就大了,因为脚本语言是解释执行的,它执行的时候不会知道具体的函数地址,怎么办?自动化接口就为此诞生了---“后绑定”。
自动化组件,其实就是实现了 IDispatch 接口的组件。IDispatch 接口有4个函数,解释语言的执行器就通过这仅有的4个函数来执行组件所提供的功能。IDispatch 接口用 IDL 形式说明如下:(注1)
[ object, uuid(00020400-0000-0000-C000-000000000046), // IDispatch 接口的 IID = IID_IDispatch pointer_default(unique) ] interface IDispatch : IUnknown { typedef [unique] IDispatch * LPDISPATCH; // 转定义 IDispatch * 为 LPDISPATCH HRESULT GetTypeInfoCount([out] UINT * pctinfo); // 有关类型库的这两个函数,咱们以后再说 HRESULT GetTypeInfo([in] UINT iTInfo,[in] LCID lcid,[out] ITypeInfo ** ppTInfo); HRESULT GetIDsOfNames( // 根据函数名字,取得函数序号(DISPID) [in] REFIID riid, [in, size_is(cNames)] LPOLESTR * rgszNames, [in] UINT cNames, [in] LCID lcid, [out, size_is(cNames)] DISPID * rgDispId ); [local] // 本地版函数 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 ); [call_as(Invoke)] // 远程版函数 HRESULT RemoteInvoke( [in] DISPID dispIdMember, [in] REFIID riid, [in] LCID lcid, [in] DWORD dwFlags, [in] DISPPARAMS * pDispParams, [out] VARIANT * pVarResult, [out] EXCEPINFO * pExcepInfo, [out] UINT * pArgErr, [in] UINT cVarRef, [in, size_is(cVarRef)] UINT * rgVarRefIdx, [in, out, size_is(cVarRef)] VARIANTARG * rgVarRef ); }
以上 IDispatch 接口函数的讲解,我们留到后回中进行介绍。如何在组件程序中实现这些函数那?还好,还好,就象 IUnknown 一样,MFC 和 ATL 都帮我们已经完成了。本回我们着重介绍组件的编写,下回则介绍组件的调用方法。