IDispatch接口原理与应用
目录:
采用ATL智能指针类调用IDispatch接口的方法:... 7
使用类COleDispatchDriver调用IDispatch的方法:... 11
前言:
尽管com接口是跨语言的,但是很多语言在使用com时更多地通过Automation技术来和com对象通信。IDispatch接口是Automation的核心技术。
尽管c++程序员并不喜欢甚至讨厌使用IDispatch接口,因为调用它实在是非常的麻烦而且易出错。但是不可否认大量的现存组件是只基于IDispatch接口技术而开发的,有时候你没有选择,而且如果你想要写一些组件能够在web上运行,你也离不开IDisptch接口,因为VBScript这样的脚本语言不会聪明到能够理解你的基于虚函数表的普通com接口。
与其躲避它,不如征服它。本文中,我将结合自己的经验和读者一起探讨IDispatch接口的各种应用。并介绍几种能够加快我们使用IDispatch接口的c++类。
IDispatch接口的定义:
参照文件oaidl.h中的定义----
MIDL_INTERFACE("00020400-0000-0000-C000-000000000046")
IDispatch : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(
/* [out] */ UINT *pctinfo) = 0;
virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(
/* [in] */ UINT iTInfo,
/* [in] */ LCID lcid,
/* [out] */ ITypeInfo **ppTInfo) = 0;
virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(
/* [in] */ REFIID riid,
/* [size_is][in] */ LPOLESTR *rgszNames,
/* [in] */ UINT cNames,
/* [in] */ LCID lcid,
/* [size_is][out] */ DISPID *rgDispId) = 0;
virtual /* [local] */ HRESULT STDMETHODCALLTYPE Invoke(
/* [in] */ DISPID dispIdMember,
/* [in] */ REFIID riid,
/* [in] */ LCID lcid,
/* [in] */ WORD wFlags,
/* [out][in] */ DISPPARAMS *pDispParams,
/* [out] */ VARIANT *pVarResult,
/* [out] */ EXCEPINFO *pExcepInfo,
/* [out] */ UINT *puArgErr) = 0;
};
我们通过IDispatch的GUID到注册表中搜索,可以搜索到如下结果:
注意在IDispatch接口GUID下面还有两个展开的GUID项,他们分别是ITypeInfo和ITypeLib接口。这两个接口在自动化应用中也是非常重要的。今后我们会经常看到他们。
IDispatch接口方法简介:
1)HRESULT STDMETHODCALLTYPE GetTypeInfoCount(
/* [out] */ UINT *pctinfo) ;
判断实现了IDispatch接口的对象是否支持类型信息,如果返回1则支持,返回0则不支持。
2) HRESULT STDMETHODCALLTYPE GetTypeInfo(
/* [in] */ UINT iTInfo,
/* [in] */ LCID lcid,
/* [out] */ ITypeInfo **ppTInfo) = 0;
获取对象的类型信息接口指针,该方法调用之前总应该先调用方法GetTypeInfoCount(…)确认是否支持类型信息。
参数iTInfo必须为0,否则该方法将返回DISP_E_BADINDEX表示失败
参数lcid传递类型信息的地域标志。IDispatch接口的方法和属性在不同的语言环境(地域标志)可以使用不同的名称,因而lcid不同可能会导致返回的ITypeInfo接口指针不同。如果我们创建的组件根据不同的地域标志对属性和方法起不同的名字,我们就要使用这个参数,否则可以忽略。
3)HRESULT STDMETHODCALLTYPE GetIDsOfNames(
/* [in] */ REFIID riid,
/* [size_is][in] */ LPOLESTR *rgszNames,
/* [in] */ UINT cNames,
/* [in] */ LCID lcid,
/* [size_is][out] */ DISPID *rgDispId)
IDispatch接口的属性实质上是方法,方法也就是成员函数,IDispatch接口把所有成员函数的入口地址放入到一个数组中,并且内部组织了一个Map,将数组索引和方法名称一一映射。我们常见的DISPID就是这些方法在数组中的索引。如果我们想调用某一个方法,我们就需要DISPID来让我们找到该方法的地址。
参数riid必须为NULL。
参数rgszNames为字符串数组,第一个字符串为方法或者属性的名称,后续的字符串为参数名称,IDispatch接口的参数也可以有名字。
参数cNames指定rgszNames数组中字符串的个数。
参数lcid传递地域标志,同GetTypeInfo方法中的参数。
参数rgDispId输出一个数组,每个数组成员对应rgszNames中的一个字符串名称。
关于DISPID的进一步说明:
typedef LONG DISPID;
typedef DISPID MEMBERID;
DISPID小于等于0的值都是有特殊意义的,如下面介绍的----
/* DISPID reserved to indicate an "unknown" name */
/* only reserved for data members (properties); reused as a method dispid below */
//如果GetIDsOfNames函数找不到与名称相对应的DISPID,返回该值
#define DISPID_UNKNOWN ( -1 )
/* DISPID reserved for the "value" property */
//如果调用时不指定方法或者属性,则使用该缺省值
#define DISPID_VALUE ( 0 )
/* The following DISPID is reserved to indicate the param
* that is the right-hand-side (or "put" value) of a PropertyPut
*/
//表明属性设置函数中某一个参数将接受新属性值
#define DISPID_PROPERTYPUT ( -3 )