COM 组件设计与应用(十一)—— IDispatch 及双接口的调用
| 一、前言 前段時(shí)間,由于工作比較忙,沒有能及時(shí)地寫作。其間收到了很多網(wǎng)友的來信詢問和鼓勵(lì),在此一并表示感謝。咳......我也需要工作來養(yǎng)家糊口呀...... 上回書介紹了兩種方法來寫自動(dòng)化(IDispatch)接口的組件程序,一是用 MFC 方式編寫“純粹”的IDispatch?接口;二是用 ATL 方式編寫“雙接口”的組件。 二、IDispatch?接口和雙接口 使用者要想調(diào)用普通的 COM 組件功能,必須要加載這個(gè)組件的類型庫(kù)(Type library)文件?tlb(比如在 VC 中使用?#import)。然而,在腳本程序中,由于腳本是被解釋執(zhí)行的,所以無法使用加載類型庫(kù)的方式進(jìn)行預(yù)編譯。那么腳本解釋器如何使用 COM 組件那?這就是自動(dòng)化(IDispatch)組件大顯身手的地方了。IDispatch?接口需要實(shí)現(xiàn)4個(gè)函數(shù),調(diào)用者只通過這4個(gè)函數(shù),就能實(shí)現(xiàn)調(diào)用自動(dòng)化組件中所有的函數(shù)。這4個(gè)函數(shù)功能如下:
從?Invoke()?函數(shù)的實(shí)現(xiàn)就可以看出,使用?IDispatch?接口的程序,其執(zhí)行效率是比較低的。ATL 從效率出發(fā),實(shí)現(xiàn)了一種叫“雙接口(dual)”的接口模式。下面我們來看看,到底什么是雙接口:
圖一、雙接口(dual)?結(jié)構(gòu)示意圖 從上圖中可以看出,所謂雙接口,其實(shí)是在一個(gè) VTAB 的虛函數(shù)表中容納了三個(gè)接口(因?yàn)槿魏谓涌诙际菑?/span>IUnknown?派生的,所以就不強(qiáng)調(diào)?IUnknown?了,叫做雙接口)。我們?nèi)绻麖娜我庖粋€(gè)接口中調(diào)用QueryInterface()得到另外的接口指針的話,其實(shí),得到的指針地址都是同一個(gè)。雙接口有什么好處那?答:好呀,多好呀,特別好呀......
三、使用方法 如果你的開發(fā)環(huán)境是?vc6.0,那么我們使用第九回中的Simple6組件為例,快去下載呀...... 如果你的開發(fā)環(huán)境是?vc.net 2003,那么用第十回中的Simple8組件為例,快去下載呀...... 嘿嘿,其實(shí)不下載也沒有關(guān)系,因?yàn)槟阒灰螺d本回的示例程序,里面已經(jīng)包含了所需的組件。但使用前不要忘了去注冊(cè)呀:regsvr32.exe simple6.dll?或?regsvr32.exe simple8.dll?(注意別忘了輸入組件的安裝目錄)。注冊(cè)成功后,就可以使用了,使用方法有:
示例一、IDispatch 調(diào)用原理篇 01.void?demo() 02.{ 03.::CoInitialize( NULL );?????// COM 初始化 04.? 05.CLSID clsid;????????????????// 通過 ProgID 得到 CLSID 06.HRESULT?hr = ::CLSIDFromProgID( L"Simple8.DispSimple.1", &clsid ); 07.ASSERT( SUCCEEDED( hr ) );??// 如果失敗,說明沒有注冊(cè)組件 08.? 09.IDispatch * pDisp = NULL;???// 由 CLSID 啟動(dòng)組件,并得到 IDispatch 指針 10.hr = ::CoCreateInstance( clsid, NULL, CLSCTX_ALL, IID_IDispatch, (LPVOID*)&pDisp ); 11.ASSERT( SUCCEEDED( hr ) );??// 如果失敗,說明沒有初始化 COM 12.? 13.LPOLESTR pwFunName = L"Add";????// 準(zhǔn)備取得 Add 函數(shù)的序號(hào) DispID 14.DISPID dispID;??????????????????// 取得的序號(hào),準(zhǔn)備保存到這里 15.hr = pDisp->GetIDsOfNames(???????// 根據(jù)函數(shù)名,取得序號(hào)的函數(shù) 16.IID_NULL, 17.&pwFunName,?????????????????// 函數(shù)名稱的數(shù)組 18.1,??????????????????????????// 函數(shù)名稱數(shù)組中的元素個(gè)數(shù) 19.LOCALE_SYSTEM_DEFAULT,??????// 使用系統(tǒng)默認(rèn)的語言環(huán)境 20.&dispID );??????????????????// 返回值 21.ASSERT( SUCCEEDED( hr ) );??????// 如果失敗,說明組件根本就沒有 ADD 函數(shù) 22.? 23.VARIANTARG v[2];????????????????????// 調(diào)用 Add(1,2) 函數(shù)所需要的參數(shù) 24.v[0].vt = VT_I4;??? v[0].lVal = 2;??// 第二個(gè)參數(shù),整數(shù)2 25.v[1].vt = VT_I4;??? v[1].lVal = 1;??// 第一個(gè)參數(shù),整數(shù)1 26.? 27.DISPPARAMS dispParams = { v, NULL, 2, 0 };??// 把參數(shù)包裝在這個(gè)結(jié)構(gòu)中 28.VARIANT vResult;????????????// 函數(shù)返回的計(jì)算結(jié)果 29.? 30.hr = pDisp->Invoke(??????????// 調(diào)用函數(shù) 31.dispID,?????????????????// 函數(shù)由 dispID 指定 32.IID_NULL, 33.LOCALE_SYSTEM_DEFAULT,??// 使用系統(tǒng)默認(rèn)的語言環(huán)境 34.DISPATCH_METHOD,????????// 調(diào)用的是方法,不是屬性 35.&dispParams,????????????// 參數(shù) 36.&vResult,???????????????// 返回值 37.NULL,???????????????????// 不考慮異常處理 38.NULL);??????????????????// 不考慮錯(cuò)誤處理 39.ASSERT( SUCCEEDED( hr ) );??// 如果失敗,說明參數(shù)傳遞錯(cuò)誤 40.? 41.CString str;????????????// 顯示一下結(jié)果 42.str.Format("1 + 2 = %d", vResult.lVal ); 43.AfxMessageBox( str ); 44.? 45.pDisp->Release();????????// 釋放接口指針 46.::CoUninitialize();?????// 釋放 COM 47.}示例二、CComDispatchDriver 智能指針包裝類的使用方法 01.void?demo() 02.{ 03.// 已經(jīng)進(jìn)行過了 COM 初始化 04.? 05.CLSID clsid;????????????????// 通過 ProgID 取得組件的 CLSID 06.HRESULT?hr = ::CLSIDFromProgID( L"Simple8.DispSimple.1", &clsid ); 07.ASSERT( SUCCEEDED( hr ) );??// 如果失敗,說明沒有注冊(cè)組件 08.? 09.CComPtr < IUnknown > spUnk;???// 由 CLSID 啟動(dòng)組件,并取得 IUnknown 指針 10.hr = ::CoCreateInstance( clsid, NULL, CLSCTX_ALL, IID_IUnknown, (LPVOID*)&spUnk ); 11.ASSERT( SUCCEEDED( hr ) ); 12.? 13.CComDispatchDriver spDisp( spUnk );?// 構(gòu)造只能指針 14.CComVariant v1(1), v2(2), vResult;??// 參數(shù) 15.hr = spDisp.Invoke2(????// 調(diào)用2個(gè)參數(shù)的函數(shù) 16.L"Add",?????????????// 函數(shù)名是 Add 17.&v1,????????????????// 第一個(gè)參數(shù),值為整數(shù)1 18.&v2,????????????????// 第二個(gè)參數(shù),值為整數(shù)2 19.&vResult);??????????// 返回值 20.ASSERT( SUCCEEDED( hr ) );??// 如果失敗,說明或者沒有 ADD 函數(shù),或者參數(shù)錯(cuò)誤 21.? 22.CString str;????????????// 顯示一下結(jié)果 23.str.Format("1 + 2 = %d", vResult.lVal ); 24.AfxMessageBox( str ); 25.}示例程序中使用了?Invoke2()函數(shù),其實(shí)你根據(jù)不同的函數(shù),還可以使用Invoke0()、Invoke1()、InvokeN()、PutProperty()、GetProperty()......等等等,的確很方便。 示例三、加載類型庫(kù),產(chǎn)生包裝類來使用 這個(gè)方法使用更簡(jiǎn)單一些,如果你觀察 MFC 幫你產(chǎn)生的包裝類的實(shí)現(xiàn),你就會(huì)發(fā)現(xiàn),其實(shí)它調(diào)用的是IDispatch?接口函數(shù)。使用?vc6.0?的朋友,步驟如下: 1、建立一個(gè) MFC 的應(yīng)用程序 2、開啟?ClassWizard,執(zhí)行?Add Class,選擇?From a type library
圖二、加載類型庫(kù) 3、然后找到你要使用的組件文件?simple6.dll(tlb?文件也可以),選擇接口后確認(rèn)
圖三、選擇類型庫(kù)中需要包裝的接口 4、在適當(dāng)?shù)牡胤捷斎胝{(diào)用代碼 01.#include "simple6.h"??? // 包裝類的頭文件 02.? 03.void?demo() 04.{ 05.// 已經(jīng)進(jìn)行過了 COM 初始化 06.? 07.IDispSimple spDisp;?????// 包裝類的對(duì)象 08.? 09.spDisp.CreateDispatch( _T("Simple6.DispSimple.1") )?//啟動(dòng)組件 10.spDisp.xxx(...);????// 調(diào)用函數(shù) 11.? 12.spDisp.ReleaseDispatch();???// 釋放接口 13.}使用?vc.net?的朋友,步驟如下: 1、建立一個(gè) MFC 的應(yīng)用程序 2、執(zhí)行菜單“添加\添加類”,選擇 MFC 分類中的“類型庫(kù)中的MFC類”
圖四、添加類型庫(kù)中的MFC類 3、選擇組件文件?simple8.dll(或?tlb?文件),并選擇需要包裝的接口
圖五、選擇文件和接口 4、在適當(dāng)?shù)奈恢幂斎胝{(diào)用代碼 01.#include "CDispSimple.h"??? // 包裝類的頭文件 02.? 03.void?demo() 04.{ 05.// 已經(jīng)進(jìn)行過了 COM 初始化 06.? 07.CDispSimple spDisp;?// 包裝類的對(duì)象 08.spDisp.CreateDispatch( _T("Simple8.DispSimple.1") )?// 啟動(dòng)組件 09.spDisp.xxx(...);????// 調(diào)用函數(shù) 10.? 11.spDisp.ReleaseDispatch();???// 釋放接口 12.}示例四、使用?#import?方式調(diào)用組件 #import?方式在第七回中已經(jīng)作過介紹,這里就不多羅嗦了。大家下載本回的示例程序后,自己去看吧。并且一定要掌握這個(gè)方法,因?yàn)樗倪\(yùn)行效率是最快的呀。 四、小結(jié) 留作業(yè)啦。在我們以前所實(shí)現(xiàn)的所有組件程序中,只添加了接口方法(函數(shù)),而沒有添加接口屬性(變量),你自己練習(xí)一下吧,很簡(jiǎn)單的,然后寫個(gè)程序調(diào)用看看。其實(shí)對(duì)于 VC 來說,調(diào)用屬性和調(diào)用方法沒有太大的區(qū)別(vc?把屬性包裝為?GetXXX()/PutXXX()或getXXX()/putXXX()的函數(shù)方式),但在另外一些語言中(比如腳本語言)則更方便,設(shè)置屬性值是:對(duì)象.屬性 = 變量或常量,獲取屬性值是:變量?=?對(duì)象.屬性。 本回書至此做一了斷,更多組件設(shè)計(jì)和使用的知識(shí),且聽下回分解...... 注1:多個(gè)自動(dòng)化接口的實(shí)現(xiàn)方法,我們以后再說。 注2:將來介紹 ITypeLib::GetTypeInfo()?的時(shí)候,大家再回味?IDispatch::GetTypeInfo()吧。 注3:在后面介紹“事件”的時(shí)候,我們會(huì)自己真正去實(shí)現(xiàn)一個(gè)?IDispatch::Invoke()?函數(shù)。 注4:介紹多個(gè)雙接口實(shí)現(xiàn)的時(shí)候,會(huì)談到這個(gè)問題。 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
總結(jié)
以上是生活随笔為你收集整理的COM 组件设计与应用(十一)—— IDispatch 及双接口的调用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C#开发COM组件
- 下一篇: BSTR、char*和CString转换