COM是由Microsoft提出的組件標準,它不只定義了組件程序之間交互的標準,並且還提供了組件程序運行所須要的環境。COM體現了組件化程序設計的思想,複雜的應用程序被設計成一些小的、功能單一的組件模型,這些組件模塊能夠在同一臺計算機或者不一樣的計算機上運行。COM是一門很是專業、系統和全面的知識,它涉及到不少的知識點,本章將只對COM進行入門介紹,同時幫助讀者掌握在Windows Embedded Compact 7環境下使用ATL編寫COM組件的方法,從而加深對COM技術的初步認識。c++
13.1 COM基本知識概述編程
13.1.1 什麼是COM安全
COM是面向對象的軟件模型,COM對象的概念有些相似於C++中對象的概念。在COM規範中,沒有對COM對象的嚴格定義,COM組件提供給客戶的是以對象形式封裝起來的實體,客戶與組件交互的實體是COM對象。服務器
COM對象有本身的屬性和方法,但這些屬性和方法都被COM封裝了起來,客戶只有通過接口才能對COM的方法進行調用,而接口是COM與外界通訊和交互的惟一途徑。多線程
13.1.2 什麼是接口併發
接口是一組邏輯上相關的函數集合,它能夠看做是指向該組函數集合的指針。編程語言
接口定義:接口定義遵循MIDL,它是Microsoft針對0SF的DCE(Distributes Computing Environment ) 規範中的IDL語言擴展。接口就是包含了一組函數的數據結構,它只負責定義函數。在對象建立後,接口就包含了接口指針和一個虛函數表,接口指針指向虛函數表(該函數表就成了一組函數指針的集合),每一個函數指針指向‘'COM對象實現」裏相應函數的實現。接口規範並不創建在任何編程語言基礎上,而任何具備足夠數據表達能力的語言,均可以對接口進行描述。 ide
COM中的全部接口都必須繼承自IUnknown接口,IUnknown接口定義了3個基本方法COM對象實現:用於實現COM對象中全部接口中的全部方法,分別爲AddRef,ReleascRef和Querylnterface,這些方法用於實現COM對象引用計數和接口查詢等功能。函數
圖13-1 接口與COM關係圖工具
13.1.3 COM基本結構
根據COM規範所創建的應用都是基於Client/Server模型的。一個完整的COM應用包括如下幾個部分:COM服務器、服務器方COM庫、客戶方COM庫和客戶程序。下面將分別對各個部分作簡要說明。
COM服務器:它是個容器,用來裝載全部的COM對象。服務器方COM庫:能夠看做是容器的管理者,負責從容器中找出相應的COM對象,創建對象的實例。
客戶方COM庫:鏈接員,負責把客戶的請求傳送到服務方,負責客戶COM的控制管理。
客戶程序:COM服務的享受者。
一次完整的COM方法調用主要包括如下幾個步驟:
(1)客戶程序通知COM庫,向COM庫指出要調用的COM對象的GUID或lID。
(2)客戶端COM庫接受客戶要求,向服務器端發送該要求,同時在客戶進程內創建該COM對象的代理DLL(之後客戶就同該代理DLL交互)。
(3)服務器COM庫接受請求,從COM庫中找出COM對象,創建該COM對象的實例(組件進程),在創建實例時,還會在實例的進程裏建立一個存根dll。組件程序將經過該存根dll與客戶端的存根dll進行交互。
(4)客戶程序調用代理dll接口方法。
(5)代理dU把請求接口、方法、參數、數據以及打包列集(marshalling)發送給存根dll。
(6)存根dll接收來自代理的數據包和散集(Unmarshalling),併發送給組件程序。
(7)組件程序處理數據,返回給存根dll。
(8)存根dll列集(marshalling),發送給代理DLL。
(9)代理DLL返回結果給客戶程序。
13.2 使用ATL建立COM示例
13.2.1 ATL對COM支持概述
ATL是活動模板庫(Active Template Library),也是一套基於c++的模板庫。使用ATL能夠快速地開發出高效、簡潔的代碼,同時爲COM組件的開發提供了最大限度的代碼自動化生成功能以及可視化支持。因爲ATL的內容很是多,所以本書只對ATL進行一個入門式的介紹。若是讀者想更全面、深刻地瞭解ATL知識,能夠查閱專門的ATL和COM書籍。
ATL實現COM接口的方式與MFC實現COM接口的方式有所不一樣,MFC是經過嵌套類來實現COM接口的,ATL則是使用多重繼承的方式實現COM接口的。多重繼承的方式彷佛比嵌套類的方式更清晰也更易使用。下面就來介紹使用ATL實現COM接口的方法。爲了方便說明,請先看下面的例子:
class ATL_NO_VTABLE_CSimple:
public CComObjectRootEx<ccomMultiThreadModel>,
publiC CComCOClass<CSimple,&CLSID_Simple>,
public IDispatchImpl<ISimple, &IID_ISimple, &LIBID_CECOMSERVERLib>
{
public:
CSimple()
{
}
DECLARE_REGISTRY_RESOURCEID(IDR_SIMPLE)
DECLARE_PROTECT_FINAL_CONSTRUCT()
BEGIN_COM(CSimple)
COM-INTERFACE.-ENTRY(ISimple)
COM_INTERFACE_-ENTRY(IDispatch)
END_COM_MAP()
//ISimple
public:
STDMETHOD(_COM_issue_errorex)(HRESULT_hr1,Iunknown *pthisl , const GUID
refiidl);
STDMETHOD(ShowSvrMsg) ();
};
以上代碼將實現ISimple接口的CSimple類定義。下面就對其中重要的代碼進行解釋說明
.CComOhjectRootEx類和CComCoOass類;
CComObjccIRootE.模板婁支持對象聚合或不聚合情形,同時它還實現了標準COM接口
的引用計數和接口查詢方法;CComObjectRootEx模板類的構造函數須要傳遞COM對像的線程模型類。在Windows Embedded Compact 7中,只支持多線程模型,即CComMuhiThreadModel類。
CComCoClass模板類爲對象定義了缺省的類廠,同時它還支持聚臺模型。CComCoCtass
模板類的定義以下:
template.< class T,
const CLSID'*pclsid=&CLSID_NULL >
class ccomCoClass
參數T表示COM對象類,參數pclsid表示COM對像類的標識。
. iDispatchlmpl模板類。
爲了在腳本語言環境中使用COM組件.COM規範規定:須要在腳本語言環境中使用的COM必須實現iDispatch接口,此時的COM接口被稱爲派發接口。在建立COM時須要選擇接口形式:custom或者dual,前者是自定義接口.也就是從IUnknown派生的接口:後者是從IDispatch派生的接口,固然IDispatch也必須從IUnknown派生。只有當接口是從IDispatch派生時,此COM組件才能夠在不支持指針及腳本語言環境下使用。CSimple類繼承於IDispatchImpl類,表示ISampic接口爲雙接口。
.COM映射表宏。
以上示例代碼的黑體部分爲COM映射表宏,它完成了接口查找的功能(Querylnterface)。用戶想暴露多少個接口,就業寫多少個COM_INTERFACE_ENTRY宏,ATL會自動用這些聲明生成一個名爲_ATL_INTMAP_FNTRY的接口表,而後在CComObjectRootBase類中提供個以這張接口表做爲參數的IntemaIQuerylmerface函數,做出正確的Querylnteface.
所以COM映射表宏能夠理解以下:完成了COM接口查找功能,而不須要再去實現Querylnterface函數。
13.2.2 ATL建立COM對象示例
在這個例子裏,將使用ATL建立一個簡單的COM。該COM提供ISimple接口,並經過ISimple接口的ShowSvrMs9方法彈出一個消息提示框。下面就分步驟介紹使用ATL建立COM對象的過程。
(1)使用VS2008新建一個智能設備IATL智能設備項目CEComServer。單擊「肯定」按鈕後,進入如圖所示的下一步向導。在該界面中,服務器類型選擇「動態連接庫(DLL)」,附加選項選擇「支持MFC」複選框。而後單擊「完成」按鈕,便創建了一個ATL COM工程。最後將編譯環境設置爲yinchengOS。建立模板如圖13-2,定義編譯環境如圖13-3,工程設置如圖13-4,定義支持如圖13-5
圖13-2程序運行效果圖
圖13-3定義編譯環境
圖13-4程序設置
圖13-5定義支持項
(2)添加Com對象
在VS2008主菜單,項目|添加類,選擇添加ATL簡單對象,名稱頁主要輸入兩類信息,分別是C++信息與COM信息。名稱設置爲sample.如圖13-6,13-7,13-8
圖13-6選擇類模板
圖13-7類名稱設置
圖13-8設置類選項並建立之
在屬性頁中來設置COM對象底層特徵,這裏主要包括如下5種屬性:
屬性l——線程模型:指定新類的實例將在何種套間中運行。這裏選擇「自由」模型,也就是經過咱們自身的線程同步來確保組件的訪問安全。
屬性2——接口類型:指定新類的接口類型。本頁框提供兩種可供選擇的接El類型:Dual(雙接口)和Custom(自定義接口)。雙接口使用typeLib列集器而且從IDispatch接口派生,自定義接口須要自定義的代理/存根而且不是從IDispatch派生。
屬性3——聚合設置:用於設置COM對象是否可以被聚合,即COM對象是否可以做爲受控的內部對象參與聚合,此設置不影響COM對象是否可以做爲外部控制對象參與聚合。
屬性4——是否支持ISupportErrorlnf0接口。若是但願在COM對象發生錯誤時拋出COM異常,那麼這個屬性是必需要選中的。
屬性5——是否支持鏈接點事件。若是選中此接口,那麼嚮導將生成一個IconnectionPoint接口的實現,它能夠向客戶端發主動發生通知事件。
設置完以上屬性後,單擊「完成」按鈕,便添加了Simple對象。
(3)爲Simple對象添加接口方法。
選中ISimple接口,單擊鼠標右鍵,在彈出的快捷菜單中選擇「添加方法(M)」項,爲Simple對象添加接口方法,如圖13-9,13-10所示。
圖13-9爲Simple對象添加接口方法
在添加了ShowSvrMsg方法後,就要來具體實現ShowSvrMsg方法了。執行ShowSvrMsg方法將簡單地彈出一個對話框,該方法的實現代碼如程序。
STDMETHODIMP CSimple::ShowSvrMsg(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: 在此添加實現代碼
::MessageBox(NULL,_T("此消息來自COM"),_T("COM測試"),MB_OK);
return S_OK;
}
圖13-10爲接口添加方法嚮導
編譯此工程並部署到模擬器後,VS2008會遠程自動註冊CEComServer。
13.2.3 建立客戶端調用CEComServer
(1)使用VS2008智能設備MFC智能設備應用程序嚮導建立一個基於對話框的應用程序CEComClient,編譯環境設置爲yinchengos。
(2)將上面建立的CEComServer工程生成的DLL添加到stdAfx.h文件中。能夠在stdAfx.h文件末尾處添加以下語句:
#import "C:\\CEComServer\\CEComServer\\yincheng_OS (x86)\\Debug\\CEComServer.dll" no_namespace
以上語句中的目錄是CEComServer項目的編譯目錄,讀者應該將該目錄修改成本身的目錄。
(3)在對話框上放置一個按鈕,用來調用ISample接口中的ShowSvrMsg方法。
//調用COM 方法
void CCEComClientDlg::OnBnClickedBtnCall()
{
HRESULT hr;
CLSID clsid;
ISimple *pSimple = NULL;
//初始化COM庫,對組件實例化
hr = CoInitializeEx(NULL,COINIT_MULTITHREADED);
//獲得CLSID
hr = CLSIDFromProgID(OLESTR("CEComServer.Simple.1"),&clsid);
if(FAILED(hr))
{
AfxMessageBox(L"未找到ID");
goto error;
}
//獲得ISimple COM接口
CoCreateInstance(clsid,NULL,CLSCTX_INPROC_SERVER,__uuidof(ISimple),(void**)&pSimple);
if(pSimple == NULL)
{
AfxMessageBox(L"接口指針失敗");
goto error ;
}
//調用ISimple方法
pSimple->ShowSvrMsg();
error:
//釋放ISimple接口
if (pSimple != NULL)
{
pSimple->Release();
pSimple = NULL;
}
//釋放COM組件庫
CoUninitialize();
}
至此,客戶端調用Windows CE Comserver示例就編寫完成了。編譯下載到模擬器中,運行效果。如圖13-11。
圖13-11效果圖
13.3 可鏈接點對象及示例
13.3.1 可鏈接點對象概述
在前面講述的COM示例中,客戶與COM組件之間的通信過程是單向的,即客戶主動使用組件提供的接口方法。在這種交互過程當中,客戶是主動的,而組件是被動的,組件經過自身暴露給客戶的接口來監聽客戶的請求,一旦收到客戶的請求便作出反應,這樣的接口被稱之爲「入接口」(incoming interface)。對於一個全面的交互過程,這樣的單向通信每每不能知足實際的須要,組件對象也應主動和客戶進行通訊。所以與「入接口」相對應,對象也能夠提供「出接口」(outgoing interface),對象經過這些出接口與客戶進行通信。
若是一個COM對象支持一個和多個出接口,那麼稱這樣的對象爲可鏈接對象(connectableob ject)。出接口包含一組成員函數,每一個成員函數表明一個通知。例如當COM對象中的某個狀態改變時,此時能夠經過COM對象向客戶程序發送一個通知。
「出接口」與「入接口」有本質的區別,「入接口」是由COM對象自己來實現的,而「出接口」則由客戶程序來實現,客戶程序實現這些接口,並把接口指針告訴對象,之後對象便利用此接口指針來與客戶程序進行通訊。在客戶程序方,實現這些接口的對象稱之爲「接收器」(sink),接收器自己也是一個COM對象,但它每每比較簡單,負責處理組件的通知。
因而可知,客戶與可鏈接對象之間的通信是雙向的,一方面,客戶按照常規的途徑調用
對象提供的接口,執行相應功能;另外一方面,對象也能夠經過它的出接口向客戶發出請求、通知或事件。從對象的一方來看,它的入接口和出接口分別承擔了這兩個通信過程:從客戶的一方來看,前一個通信過程由基本的客戶代碼完成,後一個通信過程則是由客戶方的接收器完成。所以整個通信過程涉及到三個既獨立又相關的部分:客戶、對象和接收器。明白了這三個部分之問的關係,對可鏈接對象實現的理解就簡單多了。下面將重點介紹這客戶、對象和接收器三部分問的關係。
1.可鏈接對象的基本結構
可鏈接對象能夠經過一個和多個出接口與客戶端主動進行通信。COM中約定可鏈接對象
必須實現一個lConnectionPointContainer接口,用於管理全部的出接口。每一個出接口對應一個鏈接點對象,而鏈接點對象實現了IConnectionPoint接口。客戶正是經過IConnectionPoint接口與可鏈接對象創建鏈接,此時客戶端經過接收器與可鏈接對象進行通信。
一個可鏈接對象能夠包括多個鏈接點對象,也就是多個出接口,
能夠經過IConnectionPointContainer接口來枚舉此對象所支持的全部出接口。對於每個出接口的鏈接點對象,也可使用一個枚舉器來管理它所鏈接的接收器。經過這兩個枚舉器,使得可鏈接對象支持多個出接口,且每一個出接口支持與多個接收器鏈接。
2.客戶程序與可鏈接對象的關係
以上介紹了可鏈接對象的基本結構後,下面接着介紹客戶程序和可鏈接對象的關係。首先看一下「客戶程序與可鏈接對象」的簡單關係圖。如圖13-12,13-13
圖13-12可鏈接對象的基本結構
圖13-13 客戶程序與可鏈接對象的簡單關係圖
客戶程序把接收器的接口指針傳遞給可鏈接對象,可鏈接對象將記錄下接收器的接口指針,之後在必要的時候,能夠經過此接口指針調用接收器的成員函數。這裏須要說明的是,接收器也是一個COM對象,可是它位於客戶程序內部,並不須要經過COM庫來建立,所以接收器並不須要CLSID來標識,接收器的標識和建立過程徹底是客戶程序內部的事情。對於客戶程序外部而言,接收器也是一個單獨的COM對象,它有本身的引用計數和接口查詢方法。
客戶與可鏈接對象創建鏈接的過程以下:
(1)調用pUnK—Advise(pSomeEventSet,&dwCookie),獲得鏈接點容器接口。若是調用失敗,則說明此對象不支持出接口。查找指定的鏈接點對象。
(2)調用pConnectionPoint—Advise(pSomeEventSet,&dwCookie),查找指定的連接對象。若是再也不使用鏈接點容器,那麼不管調用是否成功,都應該調用pConnection PointContainer- -Release方法,釋放鏈接點容器接口。
(3)調用pConnectionPoint—Advise(pSomeEventSet,&dwCookie), 創建與接收器的鏈接,客戶端保存鏈接標識dwCookie,以便之後斷開鏈接時使用。
(4)當客戶端要取消鏈接時,須要先調用pConnection PointoUnadvise(dwCookie)斷開與接收器的鏈接,而後再調用pConnection Point--Release釋放鏈接點對象。
對於可鏈接點對象就簡要介紹到這裏,下面將介紹一個鏈接點示例,但願經過該例子能加深讀者對它的理解。
13.3.2 鏈接點示例
1.編寫帶鏈接點事件的cOM
(1)使用VS2008新建一個智能設備IATL智能設備項目ConnectionCom,將編譯環境設置爲yinchengOS.
(2)在類視圖上單擊鼠標右鍵建立一個ATL對象,名稱爲Add,將線程模型設置爲「自由」,最後要肯定選中「鏈接點」複選框,以支持鏈接點事件。添加完Add對象後,在類視圖中,將出現Add和IAddEvents接口,後者是一個代理類,它將在客戶端中被實現。它的出現是由於選中了「鏈接點」複選框。
(3)爲IAdd接口添加一個Add(LONG a,LONG b)方法。關於爲一個接口添加方法的操做,在前面的章節中已經作了詳細介紹,所以這裏就再也不贅述了。讀者能夠參考前面章節的說明進行接El方法的添加。爲IAdd接口添加Add(LONG a,LONG b)方法的。如圖13-14
圖13-14爲IAdd接口添加一個Add(int a,int b)方法
(4)爲IAddEvents接121添加ExecutionOver(LONG IResult)方法,如圖13-7所示。該方法用來通知用戶已經執行完IAdd接口中的Add方法。
備註:在類視圖中, IAddEvents接口在ConnectionComLib目錄下。
(5)編譯項目,而後來實現鏈接點方法。具體操做以下:選中CAdd類,單擊鼠標右鍵,在彈出快捷菜單中,選擇「添加添加鏈接點」菜單項。此時,將打開下圖所示的實現鏈接點對話框。如圖13-15,13-16,13-17
圖13-15 爲一IAddEvents接口添加ExecutionOver(LONG Lresult)方法
圖13-16實現鏈接點方法
圖13-17 添加到實現鏈接點列表
在圖13-10所示的對話框中,將源接口列表中的IAddEvents添加到右邊的實現鏈接點列表中,單擊「完成」按鈕,便實現了鏈接點事件。此時CProxy類被生成,並.IAddComEvents帶有一個Fire ExecutionOver(LONG IResult)方法。這個類將關注COM對象如何調用客戶端接口。如今來實現原始IAdd接口中的Add方法。
//IAdd接口的Add方法
STDMETHODIMP Cadd::Add(LONG a,LONG b)
{
AFX MANAGE STATE(AfxGetStaticModuleState())j
Sleep(1000);
//觸發執行完畢命令
Fire_ExecutionOver(a+b);
return S_0K;
}
(6)至此,帶鏈接點事件的ATL COM組件就實現完畢了。讀者最後能夠編譯本項目並部署到模擬器中,VS2008的部署工具將自動遠程註冊connectionCom.dll組件。
下面將繼續編寫一個客戶端應用程序,以調用此COM。
2.編寫客戶端,調用帶鏈接點事件的COM
(1)使用VS2008智能設備IMFC智能設備應用程序嚮導建立一個基於對話框的應用程序ConnectionClient,編譯環境設置爲yincheng OS。
(2)設計對話框,界面如圖l3-18所示。
圖13-18對話框界面
對話框上的主要控件及其屬性設置如表13-1所示。
表13-1 對話框上的主要控件及其屬性表
lD |
屬性 |
IDC—EDT—NUMBERI |
輸入框,用於輸入數字l,對應成員變量m_numberl,類型long |
IDC—EDLNUMBER2 |
輸入框,用於輸入數字2,對應成員變量m—number2,類型long |
IDC—BTN—EXEC
|
按鈕,標題設爲「執行」。用於調用上面建立的ConnPointCom中的Add 方法,執行加法運算 |
(3)新建Csink類,繼承於一laddE.ents,CSink類用於實現jIAddEvems接口。因爲CSink類是從IAddE,ents繼承而來的,所以在頭文件中必須包古iAddEvents類的定義文件r因此應該引用ConnectionCom I程中的ConnectionComh文件a代碼以下:
*include」 \\ConnectionCom.h「
接着在csInk類中定義個私有變量ⅡLdwRefCount.用於存儲COM對象的引用計數,代碼以下:
private,
DWORD m_dwRefCount;//訪問計數變量
而後在Csink構造函數巾,將m_dwRefCount初始化爲0。
Csink::CSlnk()
{
m_dwRefCount =0;
}
(4)添加ConnetionCom組件的相關GUID定義。打開Conne,tionCom工程中的Conne.tion Comi c文件,拷貝以下代碼到Sink.cpp中,定義相關接口CUID。
const IID IID_IAdd = {0x7C20780D,0x056A,0x4B4C,{0xA0,0xCB,0x0E,0x11,0x0F,0x5C,0x68,0xCF}};
const IID LIBID_ConnectionComLib = {0x6C01534B,0x653C,0x435B,{0x8A,0x8A,0xC2,0x6B,0xC7,0x7D,0xA6,0x5F}};
const IID DIID__IAddEvents = {0x6D98CC76,0xF53F,0x4DC3,{0xA0,0xF1,0x69,0x15,0x15,0x0B,0xEF,0xED}};
const CLSID CLSID_Add = {0x16753E3A,0x3279,0x4704,{0xA0,0x5B,0x44,0xDA,0xA9,0x4A,0x5C,0x9C}};
(5)實現每個被定義在_iAddEvents接口中的方法(注意一個COM接口是一個純虛抽象類,繼承它的類必須實現它全部的方法)。JAddEvernus .接口繼承於IDispatch接口,共包括8個方法,其中的3個方法是每一個COM對象都必須實現的3個方法(接口查詢、引用計數加i和3i用計數減1).其他4個是IDispath接口定義的方法,除此以外還有一個ExecutionnOver方法·它是_IAddEvens自定義的接口方法。這8個方法的定義以下。
//1、實現_IAddEvents接口的ExecutionOver方法
STDMETHODIMP ExecutionOver(LONG lResult);
//2、實現_IAddEvents接口的QueryInterface方法
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void
**ppvObject);
//3、實現_IAddEvents接口的AddRef方法
ULONG STDMETHODCALLTYPE AddRef();
//4、實現_IAddEvents接口的Release方法
ULONG STDMETHODCALLTYPE Release();
//5,實現_IAddEvents接口的Invoke方法
HRESULT STDMETHODCALLTYPE Invoke(
/* [in] */ DISPID dispIdMember,
/* [in] */ REFIID riid,
/* [in] */ LCID lcid,
/* [in] */ WORD wFlags,
/* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams,
/* [out] */ VARIANT __RPC_FAR *pVarResult,
/* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo,
/* [out] */ UINT __RPC_FAR *puArgErr);
//6,
HRESULT STDMETHODCALLTYPE GetTypeInfoCount(
/* [out] */ UINT __RPC_FAR *pctinfo);
//7,
HRESULT STDMETHODCALLTYPE GetTypeInfo(
/* [in] */ UINT iTInfo,
/* [in] */ LCID lcid,
/* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo);
//8,
HRESULT STDMETHODCALLTYPE GetIDsOfNames(
/* [in] */ REFIID riid,
/* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames,
/* [in] */ UINT cNames,
/* [in] */ LCID lcid,
/* [size_is][out] */ DISPID __RPC_FAR *rgDispId);
此8個函數的具體實現代碼以下面程序清單所示。
//1、實現_IAddEvents接口的ExecutionOver方法
STDMETHODIMP CSink::ExecutionOver(LONG lResult)
{
CString strTemp;
strTemp.Format(L"結果是: %d", lResult);
AfxMessageBox(strTemp);
return S_OK;;
};
//2、實現_IAddEvents接口的QueryInterface方法
HRESULT STDMETHODCALLTYPE CSink::QueryInterface(REFIID iid, void
**ppvObject)
{
if (iid == DIID__IAddEvents)
{
m_dwRefCount++;
*ppvObject = (void *)this;
return S_OK;
}
if (iid == IID_IUnknown)
{
m_dwRefCount++;
*ppvObject = (void *)this;
return S_OK;
}
return E_NOINTERFACE;
}
//3、實現_IAddEvents接口的AddRef方法
ULONG STDMETHODCALLTYPE CSink::AddRef()
{
m_dwRefCount++;
return m_dwRefCount;
}
//4、實現_IAddEvents接口的Release方法
ULONG STDMETHODCALLTYPE CSink::Release()
{
ULONG l;
l = m_dwRefCount--;
if ( 0 == m_dwRefCount)
delete this;
return l;
}
//5,實現Invoke方法
HRESULT STDMETHODCALLTYPE CSink::Invoke(
/* [in] */ DISPID dispIdMember,
/* [in] */ REFIID riid,
/* [in] */ LCID lcid,
/* [in] */ WORD wFlags,
/* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams,
/* [out] */ VARIANT __RPC_FAR *pVarResult,
/* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo,
/* [out] */ UINT __RPC_FAR *puArgErr)
{
switch (dispIdMember)
{
//ExecutionOver方法
case 1:
{
LONG lResult = (pDispParams->rgvarg)->lVal;
ExecutionOver(lResult);
}
break;
default:
return DISP_E_BADINDEX;
}
}
HRESULT STDMETHODCALLTYPE CSink::GetTypeInfoCount(
/* [out] */ UINT __RPC_FAR *pctinfo)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE CSink::GetTypeInfo(
/* [in] */ UINT iTInfo,
/* [in] */ LCID lcid,
/* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE CSink::GetIDsOfNames(
/* [in] */ REFIID riid,
/* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames,
/* [in] */ UINT cNames,
/* [in] */ LCID lcid,
/* [size_is][out] */ DISPID __RPC_FAR *rgDispId)
{
return S_OK;
}
(6)爲對話框上的「執行」按鈕添加單擊事件代碼。單擊此按鈕,將實現執行加法操做的功能,單擊事件的實現代碼以下程序清單所示。
//執行按鈕單擊事件,調用IAdd的Add方法
void CConnectionClientDlg::OnBnClickedBtnExec()
{
HRESULT hr;
IUnknown *pSinkUnk;
CSink *pSink = NULL;
CComPtr<IAdd> pAdd;
//定義鏈接點容器指針
IConnectionPointContainer * pCPC;
//定義鏈接點指針
IConnectionPoint * pCP;
DWORD dwAdvise;
UpdateData(TRUE);
//初始化COM庫,對組件實例化
hr = CoInitializeEx(NULL,COINIT_MULTITHREADED);
//獲得IAdd COM接口
hr =pAdd.CoCreateInstance(CLSID_Add);
ASSERT(hr == S_OK);
//判斷IAdd接口是否有鏈接點事件,並獲得鏈接點容器對象
hr = pAdd->QueryInterface(IID_IConnectionPointContainer,(void **)&pCPC);
ASSERT(SUCCEEDED(hr));
//獲得鏈接點對象
hr = pCPC->FindConnectionPoint(DIID__IAddEvents,&pCP);
ASSERT(SUCCEEDED(hr));
//從CSink類建立一個鏈接點通知對象
pSink = new CSink();
ASSERT(pSink !=NULL);
//獲得CSink類的接口指針
hr = pSink->QueryInterface (IID_IUnknown,(void **)&pSinkUnk);
//同鏈接點對象創建鏈接
hr = pCP->Advise(pSinkUnk,&dwAdvise);
//執行IAdd接口的Add方法
pAdd->Add(m_number1 ,m_number2);
////斷開與鏈接點對象的鏈接
pCP->Unadvise(dwAdvise);
pCP->Release();
//釋放鏈接點容器對象
pCPC->Release();
//釋放COM庫
CoUninitialize();
}
此外,還須要在ConnectionClientDlg.pp文件中引用CSink的定義文件。
至此,鏈接點事件客戶端測試程序就編寫完成了。在運行客廣端測試程序以前,應確保ConnectinCom對象已經被成功註冊到模擬器上。在運行客戶端程序時,分別輸入數字12和數字24,而後單擊「執行」按鈕,此時執行按鈕便會調用COM對象IAdd的Add方法來進行加法運算,運算完成後,便會主動通知客戶端它的運算結果。如圖13-19。
圖13-19效果圖