【轉載】COM 組件設計與應用(九)——IDispatch 接口 for VC6.0

原文: http://vckbase.com/index.php/wv/1224.htmlphp

 

1、前言html

終於寫到了第九回,我也一直期盼着寫這回的內容耶,爲啥呢?由於自動化(automation)是很是經常使用、很是有用、很是精彩的一個 COM 功能。因爲 WORD、EXCEL 等 OFFICE 軟件提供了「宏」的功能,就連咱們使用的VC開發環境也提供了「宏」功能,更因爲 HTML、ASP、JSP 等都要依靠腳本(Script)的支持,更體現出了自動化接口的重要性。編輯器

若是你使用 vc6.0 的開發環境,請繼續閱讀。函數

若是你使用 vc.net 2003,請閱讀下一回。工具

2、IDispatch接口ui

若是是編譯型語言,那麼咱們可讓編譯器在編譯的時候裝載類型庫,也就是裝載接口的描述。在第七回文章當中,咱們分別使用了 #include 方法和 #import 方法來實現的。裝載了類型庫後,編譯器就知道應該如何編譯接口函數的調用了---這叫「前綁定」。可是,若是想在腳本語言中使用組件,問題就大了,由於腳本語言是解釋執行的,它執行的時候不會知道具體的函數地址,怎麼辦?自動化接口就爲此誕生了---「後綁定」。spa

自動化組件,其實就是實現了 IDispatch 接口的組件。IDispatch 接口有4個函數,解釋語言的執行器就經過這僅有的4個函數來執行組件所提供的功能。IDispatch 接口用 IDL 形式說明以下:(注1).net

[
    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 都幫咱們已經完成了。本回咱們着重介紹組件的編寫,下回則介紹組件的調用方法。設計

3、用 MFC 實現自動化組件3d

我寫的這整個系列文章---《COM 組件設計與應用》,可能是用 ATL 寫組件程序,但因爲自動化很是有用,在後續的文章中,還要給你們介紹組件的「事件」功能,還要介紹如何在 MFC 的程序中象 WORD 同樣支持「宏」的功能。這些都要用到 MFC,因此就給讀者嘮一嘮啦:-)

3-1:創建一個工做區(Workspace)

3-2:創建一個 MFC DLL 工程(Project),工程名稱爲「Simple5」

3-3:必定要選擇 automation,切記!切記!

3-4:創建新類

3-5:在新建類中支持automation

Class information - Name 你隨便寫個類名子啦

Class information - Base class 必定要從 CComTarget 派生呀,只有它才提供了 IDispatch 的支持

Automation - None 表示不支持自動化,你要選擇了它,那就白乾啦

Automation - Automation 支持自動化,但不能被直接實例化。後面在講解多個 IDispatch 的時候就用到它了,如今先不要着急。

Automation - Createable by type ID 必定要選擇這個項目,這樣咱們在後面的調用中,VB就可以CreateObject(),VC就可以CreateDispatch()對組件對象

例化了。注意一點,這個 ID 其實就是組件的 ProgID 啦。

3-6:啓動 ClassWizard,選擇 Automation 卡片,準備創建函數

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

3-8:再增長一個轉換字符串大小寫的函數 Upper()。函數返回值是 BSTR,這個沒有什麼疑問,但參數類型怎麼竟然是 LPCTSTR?在 COM 中,字符串不是應該使用 BSTR 嗎?是的,是應該使用 BSTR,但因爲咱們是用 MFC 寫自動化組件,它幫咱們進行 BSTR 和 LPCTSTR 之間的轉換了。

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

long CDispSimple::Add(long n1, long n2) 
{
	return n1 + n2;
}

BSTR CDispSimple::Upper(LPCTSTR str) 
{
	CString strResult(str);
	strResult.MakeUpper();

	return strResult.AllocSysString();
}

  

3-10:編譯註冊

若是上面的操做因爲疏忽而發生了錯誤,那麼你能夠手工進行改正。

其1、步驟<3-6>的對話窗中有「Delete」操做;

其2、你能夠打開 ODL 文件(注2)進行修改,修改時要特別當心函數的聲明中,有一個[id(n)] 的函數序號,可不要亂了;

其3、同步修改 H/CPP 中的函數聲明和函數體;

其4、在CPP文件中,根據狀況也要修改 BEGIN_DISPATCH_MAP/END_DISPATCH_MAP()函數影射宏。

正確編譯後,MFC不象ATL那樣會自動註冊。你須要手工執行 regsvr32.exe 進行註冊,或者執行菜單「Tools\Register control」

4、用 ATL 實現雙接口組件(操做方法和步驟,請參考《COM 組件設計與應用(五)》)

4-1:創建一個 ATL 工程(Project),工程名稱爲「Simple6」

4-2:按默認進行。選擇 DLL 類型、不合並代理和存根代碼、不支持MFC、不支持MTS

4-3:New Atl Object... 選擇Simple Object

4-4:輸入名稱和屬性,屬性按默認進行,也就是 dual(雙接口)方式(注3)

 

4-5:增長函數。在 ClassView 卡片中,選擇接口、鼠標右鍵菜單、Add Method...

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,那麼咱們就能夠實現函數的可變參數類型呀。怪怪個嚨,真爽!

5、腳本中調用舉例

打開「記事本」程序,輸入腳本程序,保存爲 xxx.vbs 文件。而後在資源管理器裏就能夠雙擊運行啦。

若是你有能力,也能夠用 JScript 書寫上面的程序,而後保存爲 xxx.js 文件,一樣也能夠在資源管理器裏運行。另外須要說的一點是,腳本程序文件的圖標(win 2000下)是,若是你不是這樣的(有一個軟件叫「XX 解霸」。寫這款軟件的人水平過低,它竟然使用 .vbs 的擴展名文件做爲它的數據流文件,破壞了系統默認的文件類型影射模式,咳......),那麼須要從新設置,方法是:

6、WORD 中使用舉例

6-1:錄製一段宏程序

 

6-2:選擇「鍵盤」,固然你也能夠把這個「宏」程序放到「工具欄」上去。這裏咱們隨便指定一個快捷鍵,好比Ctrl+Z

6-3:開始錄製了,下面你隨便輸入點什麼東東。而後點「中止」

6-4:接下來,咱們執行菜單,選擇這個剛剛錄製的宏,而後編輯它

6-5:點「編輯」按鈕,輸入下面的程序:

不作解釋了,你若是會一點點 VB ,就能看懂這個東東哈。而後保存關閉 VBA 的編輯器(注4)。

6-6:執行啦,執行啦,看看有什麼效果呀......

而後按快捷鍵Ctrl+Z

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

7、小結

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

注1:之後咱們描述接口函數,都採用 IDL 的形式了。

注2:ODL 文件和 IDL 相似,是MFC專門爲自動化而描述的接口文件

注3:雙接口,是支持 IDispatch 接口的一種特殊接口方式,後面立刻就要講啦

注4:VBA 是專門開發 Office 的一種語言---Visual Basic for Application

相關文章
相關標籤/搜索