程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> .NET實例教程 >> COM 組件設計與應用(九)-- IDispatch 接口 for vc6.0

COM 組件設計與應用(九)-- IDispatch 接口 for vc6.0

編輯:.NET實例教程

COM組件設計與應用(九)
IDispatch 接口 for vc6.0

作者:楊老師

下載源代碼

一、前言
    終於寫到了第九回,我也一直期盼著寫這回的內容耶,為啥呢?因為自動化(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:建立一個工作區(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 卡片,准備建立函數
歡迎光臨學網,點擊這裡查看更多文章教程 [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]


    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:編譯注冊
    如果上面的操作由於疏忽而發生了錯誤,那麼你可以手工進行改正。
其一、步驟<3-6>的對話窗中有“Delete”操作;
其二、你可以打開 ODL 文件(注2)進行修改,修改時要特別小心函數的聲明中,有一個[id(n)] 的函數序號,可不要亂了;
其三、同步修改 H/CPP 中的函數聲明和函數體;
其四、在CPP文件中,根據情況也要修改 BEGIN_DISPATCH_MAP/END_DISPATCH_MAP()函數影射宏。
    正確編譯後,MFC不象ATL那樣會自動注冊。你需要手工執行 regsvr32.exe 進行注冊,或者執行菜單“Tools\Register control”

四、用 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歡迎光臨學網,收藏本篇文章 [1] [2] [3] [4] [5] [6]  [7] [8] [9] [10]

((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下)是歡迎光臨學網,點擊這裡查看更多文章教程 [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]

,如果你不是這樣的(有一個軟件叫“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] [2] [3] [4] [5] [6] [7] [8] [9] [10]

但通過這個小例子,你可以體會出自動化組件的功能了,有夠厲害吧?!

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


注1:以後我們描述接口函數,都采用 IDL 的形式了。
注2:ODL 文件和 IDL 類似,是MFC專門為自動化而描述的接口文件
注3:雙接口,是支持 IDispatch 接口的一種特殊接口方式,後面馬上就要講啦
注4:VBA 是專門開發 Office 的一種語言---Visual Basic for Application
 

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