程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> 變體(Variant)與Dispatch調用(IDispatch)

變體(Variant)與Dispatch調用(IDispatch)

編輯:關於C++

變體(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 接口,但是反之 則不一定。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved