欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
COM 組件設計與應用(十一)

COM 組件設計與應用(十一
IDispatch 及雙接口
的調用

作者:楊老師

下載源代碼

一、前言
    前段時(shí)間,由于工作比較忙,沒(méi)有能及時(shí)地寫(xiě)作。其間收到了很多網(wǎng)友的來(lái)信詢(xún)問(wèn)和鼓勵,在此一并表示感謝???/span>......我也需要工作來(lái)養家糊口呀......

    上回書(shū)介紹了兩種方法來(lái)寫(xiě)自動(dòng)化
(IDispatch)接口的組件程序,一是用 MFC 方式編寫(xiě)“純粹”的 IDispatch 接口;二是用 ATL 方式編寫(xiě)“雙接口”的組件。

二、IDispatch 接口和雙接口
    使用者要想調用普通的 COM 組件功能,必須要加載這個(gè)組件的類(lèi)型庫(Type library)文件 tlb(比如在 VC 中使用 #import)。然而,在腳本程序中,由于腳本是被解釋執行的,所以無(wú)法使用加載類(lèi)型庫的方式進(jìn)行預編譯。那么腳本解釋器如何使用 COM 組件那?這就是自動(dòng)化(IDispatch)組件大顯身手的地方了。IDispatch
接口需要實(shí)現4個(gè)函數,調用者只通過(guò)這4個(gè)函數,就能實(shí)現調用自動(dòng)化組件中所有的函數。這4個(gè)函數功能如下:
 

HRESULT GetTypeInfoCount(
    [out] UINT * pctinfo)
組件中提供幾個(gè)類(lèi)型庫?當然一般都是一個(gè)啦。
但如果你在一個(gè)組件中實(shí)現了多個(gè)
IDispatch 接口,那就不一定啦(注1)
HRESULT GetTypeInfo(
    [in] UINT iTInfo,
    [in] LCID lcid,
    [out] ITypeInfo ** ppTInfo)
調用者通過(guò)該函數取得他想要的類(lèi)型庫。
幸好,在 99% 的情況下,我們都不用關(guān)心這兩個(gè)函數的實(shí)現,因為 MFC/ATL 都幫我們完成了默認的一個(gè)實(shí)現,如果是自己完成函數代碼,甚至可以直接返回 E_NOTIMPL 表示沒(méi)有實(shí)現。(注2)
HRESULT GetIDsOfNames(
    [in] REFIID riid,
    [in,size_is(cNames)] LPOLESTR * rgszNames,

    [
in] UINT cNames,
    [in] LCID lcid,
    [out,size_is(cNames)] DISPID * rgDispId)
根據函數名稱(chēng)取得函數序號,為調用 Invoke() 做準備。
所謂函數序號,大家去觀(guān)察雙接口 IDL 文件和 MFC 的 ODL 文件,每一個(gè)函數和屬性都會(huì )有
[id(序號)....] 這樣的描述。
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)
根據序號,執行函數。
使用 MFC/ATL 寫(xiě)的組件程序,我們也不必關(guān)心這個(gè)函數的實(shí)現。如果是自己寫(xiě)代碼,則該函數類(lèi)似如下實(shí)現:
switch(dispIdMember)
{
    case 1: .....; break;
    case 2: .....; break;
    ....
}
其實(shí),就是根據序號進(jìn)行分支調用啦。(注3)
 

    從 Invoke() 函數的實(shí)現就可以看出,使用 IDispatch 接口的程序,其執行效率是比較低的。ATL 從效率出發(fā),實(shí)現了一種叫“雙接口(dual)”的接口模式。下面我們來(lái)看看,到底什么是雙接口:


圖一、雙接口(dual)
結構示意圖

    從上圖中可以看出,所謂雙接口,其實(shí)是在一個(gè) VTAB 的虛函數表中容納了三個(gè)接口(因為任何接口都是從
IUnknown 派生的,所以就不強調 IUnknown 了,叫做雙接口)。我們如果從任意一個(gè)接口中調用 QueryInterface()得到另外的接口指針的話(huà),其實(shí),得到的指針地址都是同一個(gè)。雙接口有什么好處那?答:好呀,多好呀,特別好呀......

 
使用方式 因為 所以
腳本語(yǔ)言使用組件 解釋器只認識 IDispatch 接口 可以調用,但執行效率最低
編譯型語(yǔ)言使用組件 它認識 IDispatch 接口 可以調用,執行效率比較低
編譯型語(yǔ)言使用組件 它裝載類(lèi)型庫后,就認識了 Ixxx 接口 可以直接調用 Ixxx 函數,效率最高啦

結論

雙接口,既滿(mǎn)足腳本語(yǔ)言的使用方便性,又滿(mǎn)足編譯型語(yǔ)言的使用高效性。
于是,我們寫(xiě)的所有的 COM 組件接口,都用雙接口實(shí)現嗎?
錯!否!NO!
如果不是明確非要支持腳本的調用,則最好不要使用雙接口,因為:

如果所有函數都放在一個(gè)雙接口中,那么層次、結構、分類(lèi)不清
如果使用多個(gè)雙接口,則會(huì )產(chǎn)生其它問(wèn)題(注4)
雙接口、IDispatch接口只支持自動(dòng)化的參數類(lèi)型,使用受到限制,某些情況下很不方便嘍
還有很多弊病呦,不過(guò)現在我想不起來(lái)嘍......

三、使用方法
    如果你的開(kāi)發(fā)環(huán)境是 vc6.0,那么我們使用第九回中的Simple6組件為例,快去下載呀......
    如果你的開(kāi)發(fā)環(huán)境是 vc.net 2003,那么用第十回中的Simple8組件為例,快去下載呀......
    嘿嘿,其實(shí)不下載也沒(méi)有關(guān)系,因為你只要下載本回的示例程序,里面已經(jīng)包含了所需的組件。但使用前不要忘了去注冊呀:regsvr32.exe simple6.dll regsvr32.exe simple8.dll
(注意別忘了輸入組件的安裝目錄)。注冊成功后,就可以使用了,使用方法有:
 

示例程序 自動(dòng)化組件的使用方式 簡(jiǎn)要說(shuō)明
示例0 在腳本中調用 第九回/第十回中,已經(jīng)做了介紹
示例1 使用 API 方式調用 揭示 IDispatch 的調用原理,但傻子才去這么使用那,會(huì )累死了
示例2 使用 CComDispatchDriver 的智能指針包裝類(lèi) 比直接使用 API 方式要簡(jiǎn)單多啦,這個(gè)不錯!
示例3 使用 MFC 裝載類(lèi)型庫的包裝方式 簡(jiǎn)單!好用!常用!但它本質(zhì)上是使用 IDispatch 接口,所以執行效率稍差
示例4 使用 #import 方式加載類(lèi)型庫方式 #import 方式使用組件,咱們在第七回中講過(guò)啦。常用!對雙接口組件,直接調用自定義接口函數,不再經(jīng)過(guò) IDispatch,因此執行效率最高啦
示例x vb、java、c#、bcb、delphi....... 反正我不會(huì ),自己去請教高人去吧 :-(

示例一、IDispatch 調用原理篇

void demo(){::CoInitialize( NULL );		// COM 初始化CLSID clsid;				// 通過(guò) ProgID 得到 CLSIDHRESULT hr = ::CLSIDFromProgID( L"Simple8.DispSimple.1", &clsid );ASSERT( SUCCEEDED( hr ) );	// 如果失敗,說(shuō)明沒(méi)有注冊組件IDispatch * pDisp = NULL;	// 由 CLSID 啟動(dòng)組件,并得到 IDispatch 指針hr = ::CoCreateInstance( clsid, NULL, CLSCTX_ALL, IID_IDispatch, (LPVOID *)&pDisp );ASSERT( SUCCEEDED( hr ) );	// 如果失敗,說(shuō)明沒(méi)有初始化 COMLPOLESTR pwFunName = L"Add";	// 準備取得 Add 函數的序號 DispIDDISPID dispID;					// 取得的序號,準備保存到這里hr = pDisp->GetIDsOfNames(		// 根據函數名,取得序號的函數IID_NULL,&pwFunName,					// 函數名稱(chēng)的數組1,							// 函數名稱(chēng)數組中的元素個(gè)數LOCALE_SYSTEM_DEFAULT,		// 使用系統默認的語(yǔ)言環(huán)境&dispID );					// 返回值ASSERT( SUCCEEDED( hr ) );		// 如果失敗,說(shuō)明組件根本就沒(méi)有 ADD 函數VARIANTARG v[2];					// 調用 Add(1,2) 函數所需要的參數v[0].vt = VT_I4;	v[0].lVal = 2;	// 第二個(gè)參數,整數2v[1].vt = VT_I4;	v[1].lVal = 1;	// 第一個(gè)參數,整數1DISPPARAMS dispParams = { v, NULL, 2, 0 };	// 把參數包裝在這個(gè)結構中VARIANT vResult;			// 函數返回的計算結果hr = pDisp->Invoke(			// 調用函數dispID,					// 函數由 dispID 指定IID_NULL,LOCALE_SYSTEM_DEFAULT,	// 使用系統默認的語(yǔ)言環(huán)境DISPATCH_METHOD,		// 調用的是方法,不是屬性&dispParams,			// 參數&vResult,				// 返回值NULL,					// 不考慮異常處理NULL);					// 不考慮錯誤處理ASSERT( SUCCEEDED( hr ) );	// 如果失敗,說(shuō)明參數傳遞錯誤CString str;			// 顯示一下結果str.Format("1 + 2 = %d", vResult.lVal );AfxMessageBox( str );pDisp->Release();		// 釋放接口指針::CoUninitialize();		// 釋放 COM}
示例二、CComDispatchDriver 智能指針包裝類(lèi)的使用方法
void demo(){// 已經(jīng)進(jìn)行過(guò)了 COM 初始化CLSID clsid;				// 通過(guò) ProgID 取得組件的 CLSIDHRESULT hr = ::CLSIDFromProgID( L"Simple8.DispSimple.1", &clsid );ASSERT( SUCCEEDED( hr ) );	// 如果失敗,說(shuō)明沒(méi)有注冊組件CComPtr < IUnknown > spUnk;	// 由 CLSID 啟動(dòng)組件,并取得 IUnknown 指針hr = ::CoCreateInstance( clsid, NULL, CLSCTX_ALL, IID_IUnknown, (LPVOID *)&spUnk );ASSERT( SUCCEEDED( hr ) );CComDispatchDriver spDisp( spUnk );	// 構造只能指針CComVariant v1(1), v2(2), vResult;	// 參數hr = spDisp.Invoke2(	// 調用2個(gè)參數的函數L"Add",				// 函數名是 Add&v1,				// 第一個(gè)參數,值為整數1&v2,				// 第二個(gè)參數,值為整數2&vResult);			// 返回值ASSERT( SUCCEEDED( hr ) );	// 如果失敗,說(shuō)明或者沒(méi)有 ADD 函數,或者參數錯誤CString str;			// 顯示一下結果str.Format("1 + 2 = %d", vResult.lVal );AfxMessageBox( str );}
示例程序中使用了 Invoke2()函數,其實(shí)你根據不同的函數,還可以使用 Invoke0()、Invoke1()、InvokeN()、PutProperty()、GetProperty()......等等等,的確很方便。

示例三、加載類(lèi)型庫,產(chǎn)生包裝類(lèi)來(lái)使用
    這個(gè)方法使用更簡(jiǎn)單一些,如果你觀(guān)察 MFC 幫你產(chǎn)生的包裝類(lèi)的實(shí)現,你就會(huì )發(fā)現,其實(shí)它調用的是 IDispatch 接口函數。使用 vc6.0 的朋友,步驟如下:
1、建立一個(gè) MFC 的應用程序
2、開(kāi)啟 ClassWizard,執行 Add Class,選擇
From a type library


圖二、加載類(lèi)型庫

3、然后找到你要使用的組件文件 simple6.dll(tlb 文件也可以),選擇接口后確認


圖三、選擇類(lèi)型庫中需要包裝的接口

4、在適當的地方輸入調用代碼
#include "simple6.h"	// 包裝類(lèi)的頭文件void demo(){// 已經(jīng)進(jìn)行過(guò)了 COM 初始化IDispSimple spDisp;		// 包裝類(lèi)的對象spDisp.CreateDispatch( _T("Simple6.DispSimple.1") )	//啟動(dòng)組件spDisp.xxx(...);	// 調用函數spDisp.ReleaseDispatch();	// 釋放接口}
    使用 vc.net 的朋友,步驟如下:
1、建立一個(gè) MFC 的應用程序
2、執行菜單“添加\添加類(lèi)”,選擇 MFC 分類(lèi)中的“類(lèi)型庫中的MFC類(lèi)”


圖四、添加類(lèi)型庫中的MFC類(lèi)

3、選擇組件文件 simple8.dll(tlb 文件),并選擇需要包裝的接口


圖五、選擇文件和接口

4、在適當的位置輸入調用代碼
#include "CDispSimple.h"	// 包裝類(lèi)的頭文件void demo(){// 已經(jīng)進(jìn)行過(guò)了 COM 初始化CDispSimple spDisp;	// 包裝類(lèi)的對象spDisp.CreateDispatch( _T("Simple8.DispSimple.1") )	// 啟動(dòng)組件spDisp.xxx(...);	// 調用函數spDisp.ReleaseDispatch();	// 釋放接口}
示例四、使用 #import 方式調用組件
    #import 方式在
第七回中已經(jīng)作過(guò)介紹,這里就不多羅嗦了。大家下載本回的示例程序后,自己去看吧。并且一定要掌握這個(gè)方法,因為它的運行效率是最快的呀。

四、小結
    留作業(yè)啦。在我們以前所實(shí)現的所有組件程序中,只添加了接口方法(函數),而沒(méi)有添加接口屬性(變量),你自己練習一下吧,很簡(jiǎn)單的,然后寫(xiě)個(gè)程序調用看看。其實(shí)對于 VC 來(lái)說(shuō),調用屬性和調用方法沒(méi)有太大的區別(vc 把屬性包裝為 GetXXX()/PutXXX()getXXX()/putXXX()的函數方式),但在另外一些語(yǔ)言中(比如腳本語(yǔ)言)則更方便,設置屬性值是:對象.屬性 = 變量或常量,獲取屬性值是:變量 = 對象.屬性。
    本回書(shū)至此做一了斷,更多組件設計和使用的知識,且聽(tīng)下回分解......

注1:多個(gè)自動(dòng)化接口的實(shí)現方法,我們以后再說(shuō)。
注2:將來(lái)介紹 ITypeLib::GetTypeInfo
() 的時(shí)候,大家再回味 IDispatch::GetTypeInfo()
吧。
注3:在后面介紹“事件”的時(shí)候,我們會(huì )自己真正去實(shí)現一個(gè)
IDispatch::Invoke()
函數。
注4:介紹多個(gè)雙接口實(shí)現的時(shí)候,會(huì )談到這個(gè)問(wèn)題。
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
[筆記](méi)COM組件初識
COM編程技術(shù)基礎之四
IE內核瀏覽器開(kāi)發(fā)筆記
VC6中用DOM遍歷網(wǎng)頁(yè)中的元素
VC知識庫文章 - COM 組件設計與應用(四)——簡(jiǎn)單調用組件
一知半解與自定義COM接口
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久