当前位置:首页 >> 编程开发 >> Visual C++ >> 内容

变体(Variant)与Dispatch调用(IDispatch)

时间:2015/5/19 21:33:25 作者:平凡之路 来源:xuhantao.com 浏览:

变体(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的内置支持的。

IDispatch与双接口

在我看来, 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 接口,但是反之 则不一定。

相关文章
  • 没有相关文章
共有评论 0相关评论
发表我的评论
  • 大名:
  • 内容:
  • 徐汉涛(www.xuhantao.com) © 2024 版权所有 All Rights Reserved.
  • 部分内容来自网络,如有侵权请联系站长尽快处理 站长QQ:965898558(广告及站内业务受理) 网站备案号:蒙ICP备15000590号-1