自動化(Automation)基礎概念:變體(Variant)與Dispatch調用(IDispatch)

來源:互聯網
上載者:User
變體(Variant)與Dispatch調用(IDispatch) 

許式偉 (著作權聲明)
2007-3-14

在上一篇,我們解釋了COM技術基礎:COM組件(Component)與介面(Interface)。這裡我們聊聊COM技術中的經常會遇到的兩個概念:變體(Variant)和IDispatch介面。

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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.