IDispatch 接口方法的调用(领悟版)_com_dispatch_method解释
在COM开发中免不了要使用 IDispatch 接口,这个接口在脚本中对应的就是 Object 对象,经常在调用一个对象的方法或者使用他的属性的时候最容易的方法就是确切的得到这个对象的接口,这样调用起来方便多了,效率也好. 但是天不遂人愿啊,经常有些情况下不方便或者根本就得不到确定的接口,不得已啊只好硬着头皮写 IDispatch::GetIDsOfNames IDispatch::Invoke ,不仅写起来麻烦,连传参数都要注意,这个是从右到左传,最恐怖的恐怕还是”千万别内存泄露了”,苦恼啊~~
话说俺这次又要用到"可爱"的IDispatch接口了,是可以通过引入类型库来得到确定的接口类型,可是就因为这么一个函数调用引入类型库太夸张了,算了还是IDispatch::Invoke吧. 不过这个也确实有点麻烦,有没有个简单点的方法呢? 嘿嘿~~皇天不负有心人啊,终于让俺找到了,在excel上编写插件来操作excel时需要引入excel的类型库,这个excel有点不厚道设计的时候那些可以方便调用的方法都是在 I*** 中,这些个接口俺是没搞清楚如何能得到,但是如果你在 #import 中没有使用 raw_interfaces_only 选项,那在程序中调用也是相当方便的啊,方便到跟写脚本都没分别的地步了,爽啊,看他如何实现的吧~~ 打开 excel.tli 文件(这个家伙个头很大),发现很多调用并没有使用俺很熟悉的 raw_<Method> 的形式,这些个应该就是通过IDispatch接口来调用的了,随便拿一个吧~
1#pragma implementation_key(263)
2inline void Excel::Font::PutBackground ( const _variant_t & _arg1 ) {
3 _com_dispatch_method(this, 0xb4, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL,
4 L”\x000c”, &_arg1);
5}
应该想到了吧,他就是使用 _com_dispatch_method 这个东西来调用的.OK,上网找点资料
可惜失望啊,网上对这个的描述竟然——没找到,囧(这哥俩长的挺像)
~不过还是有个地方有点描述,上网址:http://support.microsoft.com/kb/178845 先看看这里对这个函数的描述:
示例代码显示无出处函数 _com_dispatch_method(), 它是的 C 运行时库函数调用。 作为备用方案, IDispatch:: Invoke 不使用该 Helper 函数直接调用。 (这个是人家微软机器翻译的,可以通过上面的网址转成英文的看原文,俺就不粘了)
HRESULT _ _ cdecl _com_dispatch_method(IDispatch * pDisp, DISPID dispid, WORD w,
VARTYPE vt, void * pResult, const wchar_t * szFormat, …);
pDisp – IDispatch of the method’s object.
dispid – The DISPID of the method to call.
w – Always DISPATCH_METHOD.
vt – The return type. Must be a Variant type.
pResult – Pointer to the memory location to place the return value.
SzFormat – String where each byte represents the variant type of each
of the arguments being passed to the method.
这个描述确实是不详细,还是看看人家excel如何使用的吧~~ 经过俺对里面n多函数的研究(就当是研究吧,显得高深)后,终于大概明白了这个函数的用法.
其实跟他一起的哥们还有几个,在comdef.h 文件中定义的,定义如下:
1HRESULT __stdcall
2 _com_dispatch_propget(IDispatch*, DISPID, VARTYPE, void*) ;
3HRESULT __cdecl
4 _com_dispatch_propput(IDispatch*, DISPID, VARTYPE, …) ;
5HRESULT __cdecl
6 _com_dispatch_method(IDispatch*, DISPID, WORD, VARTYPE, void*,
7 const wchar_t*, …) ;
8HRESULT __stdcall
9 _com_dispatch_raw_propget(IDispatch*, DISPID, VARTYPE, void*) throw();
10HRESULT __cdecl
11 _com_dispatch_raw_propput(IDispatch*, DISPID, VARTYPE, …) throw();
12HRESULT __cdecl
13 _com_dispatch_raw_method(IDispatch*, DISPID, WORD, VARTYPE, void*,
14 const wchar_t*, …) throw();
15
下面的三个带_raw_的方法还没搞清楚是干嘛的,所以先忽略,猜测貌似除了不抛出异常外跟上面的一样.
上面三个通过名字大概可以看出来了吧,_com_dispatch_propget 取IDispatch属性 ,_com_dispatch_propput 设置IDispatch属性,_com_dispatch_method 调用方法,其实前两个我就直接把他当成设置了几个参数的_com_dispatch_method 方法了,因为属性的操作在_com_dispatch_method中都可以做.下面看看如何使用这三个函数吧~
先看第一个
HRESULT __stdcall _com_dispatch_propget(IDispatch*, DISPID, VARTYPE, void*) ;
这个是取属性用的,参数依次为:
要操作的接口指针;
操作的属性或者方法的编号,就是*.idl中方法或者属性前面的 id 属性,也可以通过GetIDsOfNames 来得到;
属性的类型,这个必须是已经分配空间的Variant中支持的类型;
用来接受结果的指针,已经分配内存的哦~~
好了这个就可以调用了简单用法如下:
1 //
2 long _result;
3 _com_dispatch_propget(pDisp,0x02,VT_I4,(void*)&_result);
4 //这时_result就存储了要得到的结果值
5 //别忘了捕获异常,否则程序可能会当掉的哦~~
下面的_com_dispatch_propput应该跟上面一样(猜测,没用过),就不写了.
主要说下 _com_dispatch_method 方法吧.
上面有个简要的介绍他的相关参数了,不过我还是再描述一遍吧(装个高手)
HRESULT _ _ cdecl _com_dispatch_method(IDispatch * pDisp, DISPID dispid, WORD w,
VARTYPE vt, void * pResult, const wchar_t * szFormat, …);
pDisp – IDispatch 指针. dispid – 要调用的方法的 DISPID .
w – 有几个取值,如下:
DISPATCH_METHOD – 表示是调用方法,
DISPATCH_PROPERTYGET – 取属性,跟第一个方法一样,
DISPATCH_PROPERTYPUT – 设置属性,跟第二个方法一样,
DISPATCH_PROPERTYPUTREF – 设置属性,为带有BY_REF标志位的属性(没用过).
vt – 方法返回值,或者取属性的返回值,必须是 VARIANT 能容纳的类型(VT_
),
当为VT_EMPTY 时表示无返回值,pResult应该传递NULL.
pResult – 同样是分配好用来存放返回值内存的的地址.
szFormat – 指示后面 … 所要传递的参数,这个下面详细介绍.
… – 用来向方法传递的参数,如果szFormat为NULL,则这个不需要传递.
szFormat参数
这个参数为wchar_t的指针,此指针用来判断要传递的参数的类型和数目,在常见调用中是如下形式:
“\x000b\x080c\x400b” 形如这样的字符串,因为wchar_t是16位的数据,在其中 低八位表示参数的类型,跟VARTYPE定义的类型一致,如 VT_I4 就是 0x03, 例子中的 \x000b 就表示是 VT_BOOL 类型,如果是表示VT_BYREFF方式传递的话,需要与VT_BYREF 相活 即 VT_BOOL | VT_BYREF (\x400b),此时的参数应该传递VARIANT_BOOL
类型,szFormat中每个非NULL字符表示一个参数,有字符串有多长就表示有多少个参数,其中的\x080c 根据规律来看应该表示是可选参数,即如果在idl中定义时有 option 属性的参数需要 设置此标志位,如 VT_VARIANT | 0x0800 即 \x080c .
这样后面的… 就应该传递相应的参数,否则肯定会破坏函数的堆栈,跟调用printf应该是一样了,这个没找到文档,不过猜测如果有文档肯定要写上,如果szFormat参数描述的参数跟实际传递的参数不一样那么将是未定义行为。
例子:
在idl中方法的定义如下:
[id(1), helpstring(“方法BindPic”)]
HRESULT BindPic([in] IDispatch
pic);
C++类中的声明:
STDMETHOD(BindPic)(IDispatch
pic);
调用方式:
_com_dispatch_method((LPDISPATCH)pDisObj,0x01,DISPATCH_METHOD,
VT_EMPTY,NULL,L”\x0009″,(LPDISPATCH)pic);