COM組件設計與應用10 – IDispatch 介面 for vc.net

來源:互聯網
上載者:User

下載原始碼

一、前言
    終於寫到了第十回,我也一直期盼著寫這回的內容耶,為啥呢?因為自動化(automation)是非常常用、非常有用、非常精彩的一個 COM 功能。由於 WORD、EXCEL 等 OFFICE 軟體提供了“宏”的功能,就連我們使用的VC開發環境也提供了“宏”功能,更由於 HTML、ASP、JSP 等都要依靠指令碼(Script)的支援,更體現出了自動化介面的重要性。
    如果你使用 vc6.0 的開發環境,請閱讀前一回。
    如果你使用 vc.net 2003,請繼續......

二、IDispatch介面
    如果是編譯型語言,那麼我們可以讓編譯器在編譯的時候裝載類型庫,也就是裝載介面的描述。在第七迴文章當中,我們分別使用了 #include 方法和 #import 方法來實現的。裝載了類型庫後,編譯器就知道應該如何編譯介面函數的調用了---這叫“前綁定”。但是,如果想在指令碼語言中使用組件,問題就大了,因為指令碼語言是解釋執行的,它執行的時候不會知道具體的函數地址,怎麼辦?自動化介面就為此誕生了---“後綁定”。
    自動化組件,其實就是實現了 IDispatch 介面的組件。IDispatch 介面有4個函數,解釋語言的執行器就通過這僅有的4個函數來動作項目所提供的功能。IDispatch 介面用 IDL 形式說明如下:(注1)

[    object,    uuid(00020400-0000-0000-C000-000000000046),// IDispatch 介面的 IID = IID_IDispatch    pointer_default(unique)]interface IDispatch : IUnknown{    typedef [unique] IDispatch * LPDISPATCH;// 轉定義 IDispatch * 為 LPDISPATCH    HRESULT GetTypeInfoCount([out] UINT * pctinfo);// 有關類型庫的這兩個函數,咱們以後再說    HRESULT GetTypeInfo([in] UINT iTInfo,[in] LCID lcid,[out] ITypeInfo ** ppTInfo);    HRESULT GetIDsOfNames(// 根據函數名字,取得函數序號(DISPID)                [in] REFIID riid,                [in, size_is(cNames)] LPOLESTR * rgszNames,                [in] UINT cNames,                [in] LCID lcid,                [out, size_is(cNames)] DISPID * rgDispId            );    [local]// 本地版函數    HRESULT Invoke(// 根據函數序號,解釋執行函數功能                [in] DISPID dispIdMember,                [in] REFIID riid,                [in] LCID lcid,                [in] WORD wFlags,                [in, out] DISPPARAMS * pDispParams,                [out] VARIANT * pVarResult,                [out] EXCEPINFO * pExcepInfo,                [out] UINT * puArgErr            );    [call_as(Invoke)]// 遠程版函數    HRESULT RemoteInvoke(                [in] DISPID dispIdMember,                [in] REFIID riid,                [in] LCID lcid,                [in] DWORD dwFlags,                [in] DISPPARAMS * pDispParams,                [out] VARIANT * pVarResult,                [out] EXCEPINFO * pExcepInfo,                [out] UINT * pArgErr,                [in] UINT cVarRef,                [in, size_is(cVarRef)] UINT * rgVarRefIdx,                 [in, out, size_is(cVarRef)] VARIANTARG * rgVarRef            );}

以上 IDispatch 介面函數的講解,我們留到後回中進行介紹。如何在組件程式中實現這些函數那?還好,還好,就象 IUnknown 一樣,MFC 和 ATL 都幫我們已經完成了。本回我們著重介紹組件的編寫,下回則介紹組件的調用方法。

三、用 MFC 實現自動化組件
    我寫的這整個系列文章---《COM 組件設計與應用》,多是用 ATL 寫組件程式,但由於自動化非常有用,在後續的文章中,還要給大家介紹組件的“事件”功能,還要介紹如何在 MFC 的程式中象 WORD 一樣支援“宏”的功能。這些都要用到 MFC,所以就給讀者嘮一嘮啦:-)
    3-1:建立一個解決方案
    3-2:建立一個 MFC DLL 項目,項目名稱為“Simple7”

    3-3:一定要選擇附加功能中的“自動化”,切記!切記!

    3-4:添加新類

    3-5:在建立類中支援自動化

類名 你隨便寫個類名子啦
基類 一定要從 CComTarget 派生呀,只有它才提供了 IDispatch 的支援
自動化 - 無 表示不支援自動化,你要選擇了它,那就白乾啦
自動化 - 自動化 支援自動化,但不能被直接執行個體化。後面在講解多個 IDispatch 的時候就用到它了,現在先不要著急。
自動化 - 可按類型ID建立 一定要選擇這個項目,這樣我們在後面的調用中,VB就能夠CreateObject(),VC就能夠CreateDispatch()對組件對象執行個體化了。注意一點,這個 ID 其實就是組件的 ProgID 啦。

    3-6:選擇介面,添加函數

    3-7:添加函數。我們要寫一個整數加法函數Add()。

    3-8:再增加一個轉換字串大小寫函數 Upper()。

    3-9:好了,下面開始輸入程式碼:

LONG CDispSimple::Add(LONG n1, LONG n2){AFX_MANAGE_STATE(AfxGetStaticModuleState());return n1 + n2;}BSTR CDispSimple::Upper(LPCTSTR str){AFX_MANAGE_STATE(AfxGetStaticModuleState());CString strResult(str);strResult.MakeUpper();return strResult.AllocSysString();}

    3-10:編譯註冊

    如果上面的操作由於疏忽而發生了錯誤,那麼你可以手工進行改正。
其一、你可以開啟 IDL 檔案進行修改,修改時要特別小心函數的聲明中,有一個[id(n)] 的函數序號,可不要亂了;
其二、同步修改 H/CPP 中的函式宣告和函數體;
其三、在CPP檔案中,根據情況也要修改 BEGIN_DISPATCH_MAP/END_DISPATCH_MAP()函數影射宏。
    正確編譯後,vc.net 2003 比 vc6.0 要聰明多了,它會自動註冊組件。如果複製到其它電腦上,你也需要手工執行 regsvr32.exe 進行註冊。

四、用 ATL 實現雙介面組件(操作方法和步驟,請參考《COM 組件設計與應用(六)》)
    4-1:建立一個 ATL 項目,項目名稱為“Simple8”
    4-2:選擇 DLL 類型、非屬性方式、不要選擇任何附加選項
    4-3:添加新類,選擇ATL 的簡單對象
    4-4:輸入簡稱和選項,選項按預設進行,也就是雙重介面方式(注2)

    4-5:增加函數。選擇介面、滑鼠右鍵菜單、添加方法...
Add([in] VARIANT v1, [in] VARIANT v2, [out, retval] VARIANT * pVal);
Upper([in] BSTR str, [out,retval] BSTR * pVal);
    關於Add()函數,你依然可以使用 Add([in] long n1, [in] long n2, [out,retval] long * pVal) 方式。但這次我們沒有使用 long ,而是使用了 VARIANT 做參數和傳回值。這裡我先賣個關子,往下看,就知道使用 VARIANT 的精彩之處了。

    4-6:完成代碼

STDMETHODIMP CDispSimple::Add(VARIANT v1, VARIANT v2, VARIANT *pVal){::VariantInit( pVal );// 永遠初始化傳回值是個好習慣CComVariant v_1( v1 );CComVariant v_2( v2 );if((v1.vt & VT_I4) && (v2.vt & VT_I4) )// 如果都是整數類型{// 這裡比較沒有使用 == ,而使用了運算子 & ,你知道這是為什麼嗎?v_1.ChangeType( VT_I4 );// 轉換為整數v_2.ChangeType( VT_I4 );// 轉換為整數pVal->vt = VT_I4;pVal->lVal = v_1.lVal + v_2.lVal;// 加法}else{v_1.ChangeType( VT_BSTR );// 轉換為字串v_2.ChangeType( VT_BSTR );// 轉換為字串CComBSTR bstr( v_1.bstrVal );bstr.AppendBSTR( v_2.bstrVal );// 字串串連pVal->vt = VT_BSTR;pVal->bstrVal = bstr.Detach();}return S_OK;}STDMETHODIMP CDispSimple::Upper(BSTR str, BSTR *pVal){*pVal = NULL;// 永遠初始化傳回值是個好習慣CComBSTR s(str);s.ToUpper();// 轉換為大寫*pVal = s.Copy();return S_OK;}

    剛才賣的關子,現在開始揭密了......加法函數Add()不使用long類型,而使用VARIANT的好處是:函數內部動態判斷參數類型,如果是整數則進行整數加法,如果是字串,則進行字串加法(字串加法就是字串串連哈)。也就是說,如果參數是VARIANT,那麼我們就可以實現函數的可變參數類型呀。怪怪個嚨,真爽!

五、指令碼中調用舉例
    開啟“記事本”程式,輸入指令碼程式,儲存為 xxx.vbs 檔案。然後在資源管理員裡就可以雙擊運行啦。

如果你有能力,也可以用 JScript 書寫上面的程式,然後儲存為 xxx.js 檔案,同樣也可以在資源管理員裡運行。另外需要說明的一點是,指令碼程式檔案的表徵圖(win 2000下)是,如果你不是這樣的(有一個軟體叫“XX 解霸”。寫這款軟體的人水平太低,它居然使用 .vbs 的副檔名檔案作為它的資料流檔案,破壞了系統預設的檔案類型影射模式,咳......),那麼需要重新設定,方法是:

六、WORD 中使用舉例

    6-1:錄製一段宏程式

    6-2:選擇“鍵盤”,當然你也可以把這個“宏”程式放到“工具列”上去。這裡我們隨便指定一個快速鍵,比如Ctrl+Z


    6-3:開始錄製了,下面你隨便輸入焦點什麼東東。然後點“停止”
    6-4:接下來,我們執行菜單,選擇這個剛剛錄製的宏,然後編輯它

    6-5:點“編輯”按鈕,輸入下面的程式:

不做解釋了,你如果會一點點 VB ,就能看懂這個東東哈。然後儲存關閉 VBA 的編輯器(注4)。
    6-6:執行啦,執行啦,看看有什麼效果呀......

然後按快速鍵Ctrl+Z

    你已經擴充了 MS WORD 的功能啦,嘿啦啦啦啦,嘿啦啦啦,天空出彩霞呀......我們只是舉了一個簡單的例子,其實這個例子並沒有什麼實際應用的意義,因為人家 WORD 本身就有大小寫轉換功能。但通過這個小例子,你可以體會出自動化組件的功能了,有夠厲害吧?!

七、小結
    沒小結!嘿嘿......上當嘍:-)

注1:以後我們描述介面函數,都採用 IDL 的形式了。
注2:雙介面,是支援 IDispatch 介面的一種特殊介面方式,後面馬上就要講啦
注3:VBA 是專門開發 Office 的一種語言---Visual Basic for Application
 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.