COM的通信原理及ATL的通信操作 看過的一篇非常好的講解通訊機制的文章

COM的通信原理及ATL的通信操作

一、             COM的通信原理

1、  通信模型:一般,我們只使用客戶端程序到組件的通信,並且這種通信是通過組件的接口來實現的。現在,我們講一下服務器到客戶端之間如何打開一個雙向通信通道,並提供一個功能更加強大的通信環境。按下面的方法可以提供一個具備回調功能(或稱通知)的組件:

a)         在一個組件中描述幾個接口,其中一部分接口由組件實現(如IMath),一部分接口則由客戶端程序實現(如ICallback)。

b)        在客戶端程序中,使用自己喜歡的技術實現一個接口,並由組件來描述(如iCallback)。

c)        組件在其中的一個入站接口上實現一個方法(如IMath::Advise),客戶端程序可以通過該方法傳送它的一個接口指針(ICallback)。

d)              然後組件通過客戶端實現的接口調用接口方法,爲客戶端程序提供通知消息。

2、  引入和引出接口

COM使用incoming interface(引入接口)和outing interface(引出接口)兩個術語,來描述組件可以支持的兩種不同類型的接口。一個引入接口是指由組件實現的接口,如IMath是個引入接口,因爲它是由你的組件來實現的。一個引出接口是指在組件的類型庫中描述的接口,但是它實際上是由math組件的客戶端程序實現的。

3、  示例

interface ICallback:IDispatch

[

    [id(1),helpstring("顯示求和結果")] HRESULT Show([in] long sum);

]

interface IMath:IDispatch

[

    [id(1),helpstring("求和並顯示")] HRESULT Add([in] long num1,[in] long num2,[out,retval] long* ret);

    [id(2),helpstring("添加引出接口") HRESULT Advise([in] ICallback* pCallback);

    [id(3),helpstring("釋放接口") HRESULT Unadvise();

]

class CMath:IMath

{

    CComPtr<ICallback> m_pCallback;

    STDMETHODIMP Add(long num1,long num2)

    {

        long ret=num1+num2;

        if(m_pCallback)

            m_pCallback->Show(ret);

        return S_OK;

    }

    STDMETHODIMP Advise(ICallback* pCallback)

    {

        m_pCallback=pCallback;

        pCallback->AddRef();

        return S_OK;

    }

    STDMETHODIMP Unadvise()

    {

        m_pCallback->Release();

        m_pCallback=0;

        return S_OK;

    }

}  

客戶端程序首先實現ICallback接口中的函數,並把ICallback的實現類通過Advise傳給CMath,這樣當進行加法時,就能通知客戶端了,這就是COM的通信原理。

二、             ATL通信方法

ATL提供了IDispEventSimpleImpl和IDispEventImpl兩個模板類,這兩個模板類可用於在 ATL 類中提供連接點接收器支持,爲事件調度接口提供了實現,我們只需要對要接收的事件方法提供實現。這些連接點接收器是用事件接收映射(由類提供)來映射的。

1           若要正確地實現類的連接點接收器,必須完成以下步驟:

1.1         爲每個外部對象導入類型庫 (如:#import "progid:SendEvent.MyMath" raw_interfaces_only, no_namespace, named_guids),

1.2         繼承 IDispEventImpl接口(如public:IDispEventSimpleImpl<1,CSumDlg,&DIID_IMathEvents>),

或 繼承IDispEventSimpleImpl 接口(如IDispEventImpl<1,CSumDlg,&DIID_IMathEvents,&LIBID_SendEvent,1,0>)。

1.3         聲明事件接收映射 ,在類中添加BEGIN_SINK_MAPclassname)、END_SINK_MAP()宏,

1.4         IDispEventSimpleImpl都必須添加一個宏SINK_ENTRY_INFO去實現事件接收映射。如

BEGIN_SINK_MAP(CSumDlg)

   SINK_ENTRY_INFO(1,DIID_IMathEvents,1,OnShow,&ShowInfo)

END_SINK_MAP()

1.5            IDispEventImpl都必須添加一個宏SINK_ENTRYSINK_ENTRY_EX去實現事件接收映射。如

BEGIN_SINK_MAP(CSumDlg)

   SINK_ENTRY_EX(1,DIID_IMathEvents,1,OnShow)

END_SINK_MAP()

1.6         實現事件處理函數,如實現OnShow函數。

1.7         通知(調用DispEventAdvise與數據源建立連接)。

1.8         和取消通知連接點 (調用DispEventUnadvise斷開連接)。

2           詳細解析

2.1         IDispEventImpl繼承於IDispEventSimpleImpl,他們的大部分功能是相同的,區別僅在於IDispEventImp是從類型庫中獲取接口信息,而IDispEventSimpleImp是通過一個指向SINK_ENTRY_INFO結構體的指針獲得事件信息。

2.2            IDispEventImplIDispEventSimpleImpl的參數分別爲

IDispEventImpl<
   UINT nID,
   class T,
   const IID* pdiid = &IID_NULL,
   const GUID* plibid = &GUID_NULL,
   WORD wMajor = 0,
   WORD wMinor = 0,
   class tihclass = CcomTypeInfoHolder
>
IDispEventSimpleImpl<
   UINT nID,
   class T,
   const IID* pdiid
>
其中,
nID:唯一標識數據源對象的標誌;
T:從IDispEventImpl/ IDispEventSimpleImpl派生的類;
pdiid:要接收的事件調度接口的DIID指針;
plibid:定義了事件接口的類型庫的LIBID指針;
wMajor:類型庫的主版本號;
wMinor:類型庫的次版本號;
tihclass:管理T類的類型信息的類(一般用默認)

2.3         宏的操作

2.3.1       事件接收映射必須以BEGIN_SINK_MAP(class)開頭,以END_SINK_MAP()結尾,其中class是接收事件的類。

2.3.2       SINK_ENTRY_INFO、SINK_ENTRY_INFO和SINK_ENTRY的關係爲

SINK_ENTRY_INFO(id, iid, dispid, fn, info)

#define SINK_ENTRY_EX(id, iid, dispid, fn) SINK_ENTRY_INFO(id, iid, dispid, fn, NULL)

#define SINK_ENTRY(id, dispid, fn) SINK_ENTRY_EX(id, IID_NULL, dispid, fn)

其中,

id唯一標識數據源對象的標誌,與模板類的第一個參數對應;

iid::要接收的事件調度接口的DIID指針;

dispid:事件的調度ID,與接口中方法的ID對應;

fn:事件處理函數;

info:SINK_ENTRY_INFO結構體的指針,主要包括事件的參數和返回值信息。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章