变体(Variant)与Dispatch挪用(IDispatch)
副标题#e#
变体(Variant)
Variant范例理论上可以存放任何范例的数据,这也是中文许多 人称之为“变体”的原因。对付C++这种强范例语言的措施员来说,存在变体 (Variant)这样的范例是奇怪的。可是对付哪些淡化范例观念的语言(如Visual Basic等) 来说,Variant是它们默认的范例。在VB中,假如没有用As语句声明变量,那么这个变量就是 Variant范例的。对付C++措施员来说,Variant不外是一个超巨大的布局体:
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的内置支持的。
#p#副标题#e#
IDispatch与双接口
#p#分页标题#e#
在我看来, Dispatch挪用 (IDispatch)的存在主要是剧本语言的需要。剧本语言大都属于表明型语言,其代码并不生 成呆板指令,而是边表明边执行(可能翻译成为中间代码后表明执行),这种语言凡是有这 样一个需求:就是要在不知道类(可能说组件)的具体规格环境下挪用类的要领。
Dispatch挪用(IDispatch)就是满意这种需求的一个技能规格。它用一个dispid或 者名字(字符串)暗示要挪用的要领(可能属性),其道理和Windows窗口的动静机制挺雷同 (你可以把窗口动静中的uMsg参数和这里的dispid对应起来)。IDispatch的实现者对dispid 举办分配,完成详细的成果挪用。有些剧本语言也许未必回收 IDispatch 作为它的挪用尺度 ,可是凡是是一种和 IDispatch 雷同的对象。
这种在不知道类(可能说组件)的详 细规格环境下挪用类的要领,我们称之为“晚绑定”。这是相对付C++这类编译型 语言中基于虚函数机制的挪用机制而言的,后者我们成为“早绑定”。对付虚函 数机制,它要求组件的接口是已知的,假如你不知道组件的接口,也就不知道又哪些要领可 用,更谈不上如何去挪用。
IDispatch的界说如下:
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要害字暗示:
[dual]
interface IFoo : IDispatch
{
HRESULT foo(int arg1, int arg2);
};
这里 IFoo 是一个双接口。一个双接口必然是 IDispatch 接口,可是反之 则不必然。