Windows Embedded Compact 7中的 COM編程(上)



       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-接口與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  ATLCOM支持概述

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-9Simple對象添加接口方法

在添加了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-14IAdd接口添加一個Add(int aint 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 aLONG 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

    屬性

IDCEDTNUMBERI

輸入框,用於輸入數字l,對應成員變量m_numberl,類型long

 IDCEDLNUMBER2

輸入框,用於輸入數字2,對應成員變量mnumber2,類型long

 IDCBTNEXEC

 

按鈕,標題設爲「執行」。用於調用上面建立的ConnPointCom中的Add

方法,執行加法運算

 (3)新建Csink,繼承於一laddE.entsCSink類用於實現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個方法(接口查詢、引用計數加i3i用計數減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)爲對話框上的「執行」按鈕添加單擊事件代碼。單擊此按鈕,將實現執行加法操做的功能,單擊事件的實現代碼以下程序清單所示。

   //執行按鈕單擊事件,調用IAddAdd方法

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對象IAddAdd方法來進行加法運算,運算完成後,便會主動通知客戶端它的運算結果。如圖13-19

 

13-19效果圖

相關文章
相關標籤/搜索