我是靠谱客的博主 羞涩豆芽,这篇文章主要介绍自动化(Automation)基础概念:变体(Variant)与Dispatch调用(IDispatch)变体(Variant)IDispatch与双接口,现在分享给大家,希望可以做个参考。

变体(Variant)

Variant类型理论上可以存放任何类型的数据,这也是中文很多人称之为“变体”的原因。对于C++这种强类型语言的程序员来说,存在变体(Variant)这样的类型是奇怪的。但是对于哪些淡化类型概念的语言(如Visual Basic等)来说,Variant是它们默认的类型。在VB中,如果没有用As语句声明变量,那么这个变量就是Variant类型的。对于C++程序员来说,Variant不过是一个超复杂的结构体:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
typedef /* [wire_marshal] */ struct tagVARIANT VARIANT; struct tagVARIANT     {     union          {         struct __tagVARIANT             {             VARTYPE vt;             WORD wReserved1;             WORD wReserved2;             WORD wReserved3;             union                  {                 LONGLONG llVal;                 LONG lVal;                 BYTE bVal;                 SHORT iVal;                 FLOAT fltVal;                 DOUBLE dblVal;                 VARIANT_BOOL boolVal;                 _VARIANT_BOOL bool;                 SCODE scode;                 CY cyVal;                 DATE date;                 BSTR bstrVal;                 IUnknown *punkVal;                 IDispatch *pdispVal;                 SAFEARRAY *parray;                 BYTE *pbVal;                 SHORT *piVal;                 LONG *plVal;                 LONGLONG *pllVal;                 FLOAT *pfltVal;                 DOUBLE *pdblVal;                 VARIANT_BOOL *pboolVal;                 _VARIANT_BOOL *pbool;                 SCODE *pscode;                 CY *pcyVal;                 DATE *pdate;                 BSTR *pbstrVal;                 IUnknown **ppunkVal;                 IDispatch **ppdispVal;                 SAFEARRAY **pparray;                 VARIANT *pvarVal;                 PVOID byref;                 CHAR cVal;                 USHORT uiVal;                 ULONG ulVal;                 ULONGLONG ullVal;                 INT intVal;                 UINT uintVal;                 DECIMAL *pdecVal;                 CHAR *pcVal;                 USHORT *puiVal;                 ULONG *pulVal;                 ULONGLONG *pullVal;                 INT *pintVal;                 UINT *puintVal;                 struct __tagBRECORD                     {                     PVOID pvRecord;                     IRecordInfo *pRecInfo;                     }     __VARIANT_NAME_4;                 }     __VARIANT_NAME_3;             }     __VARIANT_NAME_2;             DECIMAL decVal;         }     __VARIANT_NAME_1;     } ; 

Variant类型在解释型语言和脚本语言中应用甚广。在Visual Basic,JavaScript等身上,处处可见其身影。但是如果没有语言本省的支持,对Variant操作是复杂的。不幸的是,C/C++就是属于这种情况。这应该说与C++对新技术的慎重,以及是一种非纯商业公司控制的语言有关。其他语言如Delphi,一定要与时俱进,是一定要加Variant的内置支持的。

IDispatch与双接口

在我看来, Dispatch(派遣,发送)调用(IDispatch)的存在主要是脚本语言的需要。脚本语言多数属于解释型语言,其代码并不生成机器指令,而是边解释边执行(或者翻译成为中间代码后解释执行),这种语言通常有这样一个需求:就是要在不知道类(或者说组件)的详细规格情况下调用类的方法

Dispatch调用(IDispatch)就是满足这种需求的一个技术规格。它用一个dispid或者名字(字符串)表示要调用的方法(或者属性),其原理和Windows窗口的消息机制挺类似(你可以把窗口消息中的uMsg参数和这里的dispid对应起来)。IDispatch的实现者对dispid进行分派,完成具体的功能调用。有些脚本语言也许未必采用 IDispatch 作为它的调用标准,但是通常是一种和 IDispatch 类似的东西。

这种在不知道类(或者说组件)的详细规格情况下调用类的方法,我们称之为“晚绑定”。这是相对于C++这类编译型语言中基于虚函数机制的调用机制而言的,后者我们成为“早绑定”。对于虚函数机制,它要求组件的接口是已知的,如果你不知道组件的接口,也就不知道又哪些方法可用,更谈不上如何去调用。

IDispatch的定义如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
interface IDispatch : public IUnknown     {     public:         virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(              /* [out] */ UINT __RPC_FAR *pctinfo) = 0;                  virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(              /* [in] */ UINT iTInfo,             /* [in] */ LCID lcid,             /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo) = 0;                  virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(              /* [in] */ REFIID riid,             /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames,             /* [in] */ UINT cNames,             /* [in] */ LCID lcid,             /* [size_is][out] */ DISPID __RPC_FAR *rgDispId) = 0;                  virtual /* [local] */ HRESULT STDMETHODCALLTYPE Invoke(              /* [in] */ DISPID dispIdMember,             /* [in] */ REFIID riid,             /* [in] */ LCID lcid,             /* [in] */ WORD wFlags,             /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams,             /* [out] */ VARIANT __RPC_FAR *pVarResult,             /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo,             /* [out] */ UINT __RPC_FAR *puArgErr) = 0;              };

最后一个问题是,什么是“双接口”? 一个误区是,也许有人会把“双接口”和从IDispatch继承的接口等同起来。不过,这种理解有点片面了。

所谓“双接口”,是指哪些既实现了IDispatch接口,又实现了基于虚表调用的普通接口的特殊接口。双接口的好处在于它既适应了C++这种支持虚表(vtbl)、追求高效的语言,也支持了脚本语言。在idl文法中,双接口以dual关键字表示:

复制代码
1
2
3
4
5
[dual] interface IFoo : IDispatch {     HRESULT foo(int arg1, int arg2); };

这里 IFoo 是一个双接口。一个双接口一定是 IDispatch 接口,但是反之则不一定。

最后

以上就是羞涩豆芽最近收集整理的关于自动化(Automation)基础概念:变体(Variant)与Dispatch调用(IDispatch)变体(Variant)IDispatch与双接口的全部内容,更多相关自动化(Automation)基础概念内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(57)

评论列表共有 0 条评论

立即
投稿
返回
顶部