連接點語義
第一種說法:是一種邏輯上的反饋機制,這種機制允許對象暴露其”調用一個或者多個指定接口的能力”
第二種說法:QueryInterface允許客戶從對象中取得一個指向對象實現的接口指針,連接點允許客戶給予對象一個由客戶實現的接口指針.
在這種情形下:COM對象是源,客戶提供的方法是接收器.
源必須實現IConnectionPoint
Interface IConnectionPoint
{
HRESULT GetConnectionInterface([out] IID *pIID);
HRESULT GetConnectionPointContainer([out] IConnectionPointContainer** ppCPC);
HRESULT Advise([in] IUnknnown *punkSing, [Out] DWORD *pdwCookie);
HRESULT Unadvise([in]DWORD dwCookie);
HREUSLT EnumConnections([out] IEnumConnections** ppEnum);
}
Interface IConnectionPointContainer
{
HRESULT EnumConnectionPoints([out] IEnumConnectionPoints **ppEnum);
HRESULT FindConnectionPoint([in] REFIID riid, [out] IConnectionPoint **ppcP);
}
客戶的使用方法:
IUnKnown *pSource;
ISpeakerEvent *pSink;
DWORD dwCookie;
IConnectionPointContainer pcpc;
Hr = pSource->QueryInterface(&pcpc);
IConnectionPoint pcp;
Hr = pcpc->FindConnection(__uuidof(ISpeakerEvent));
Hr = pcp->Advise(pSink,&dwCookie);
Hr = pcp->Unadvise(dwCookie);
便捷的的宏:
AtlAdivse(psource, pSink, __uuidof(ISpeakerEvent), &dwCookie);
AtlUnadvise(psource, __uuidof(ISpeakerEvent), dwCookie);
建立可連接對象的步聚:
1:實現IConnectionPointContainer接口
Class ATL_NO_VTABLE className:
….
Public IConnectionPointContainerImpl<className>
{…..
};
2:QueryInterface對DIID_IConnectionPointContainer的請求作出響應
BEGIN_COM_MAP
…
COM_INTERFACE_ENTRY(IConnectionPointContainer)
END_COM_MAP
3:我們要為每個可連接對象支持的源接口實現IConnectionPoint
Class ATL_NO_VTABLE className:
….
Public IConnectionPointContainerImpl<className>,
Public IConnectionPointImpl<className, &DIID__對外的接口>
{…..
};
4:我們要提供一個連接映射表,也就是一個IID和連接點實現聯系起來的表.
BEGIN_CONNECTION_POINT_MAP
CONNECTION_POINT_MAP_ENTRY(DIID__對外的接口)
….
END_CONNECTION_POINT_MAP()
5:我們必須更新可連接對象在IDL文件中coClass的定義,以便指定每個源接口.每個源接口必須具有屬性,主源接口應具有[default, source]屬性.
Coclass 類廠名
{……
[default,source] dispinterface _對外接口;
};
6:一般來說,我們希望通過輔助方法為所有連接的接收器調用接收器方法.
HRESULT Fire_事件(parameter)
{
依次調用每個接收器的方法
}
可以使用IDE來生成連接點代理類.這樣我們的源可以從其派生,而不再從IConnectionPointImpl派生.
7:我們必須在適當的時機調用輔助方法.
建立接收事件的對象:
1:實現事件接收器.可先的方案有從
IDispEventSimpleImpl<UINT nID, class T, const IID *pdIID = &IID_NULL>
或者:
IDispEventImpl< UINT nID, class T, const IID *pdIID = &IID_NULL,
Const GUID*plibid= &GUID_NULL,
DWORD wMajor = 0, WORD wMinor = 0,
Class tihclass = CComTypeInfoHolder>
派生.
例如:
static const int DEFSOURCEID = 1;
class CEarPolitic;
typedef IDispEventImpl< DEFSOURCEID, CEarPolitic, &DIID__ISpeackerEvents
&LIBID_ATLINTERNALSLIB, LIBMAJOR,LIBMINOR> DefSource;
Class ATL_NO_VTABLE CEarPolitic
:public DefSource
{
….
}
2:事件接收器映射表
BEGIN_SINK_MAP(CEarPolitic)
SINK_ENTRY_EX(source, DIID, DISPID, EventHandlerFunc)
SINK_ENTRY_EX(source, DIID, DISPID, EventHandlerFunc, &info)
END_SINK_MAP()
例如:
void __stdcall OnHearPlaintiffWhisper(BSTR bstrText);//sink接口的一個方法.
_ATL_FUNC_INFO OnHearPlaintiffWhisper =
{CC_STDCALL, VT_EMPTY, 1, { VT_BSTR}};
Static const int SOURCEID = 1;
BEGIN_SINK_MAP(CEarPolitic)
SINK_ENTRY_EX(SOURCEID, DIID__對外接口, 方法的DISPID, OnHearPlaintiffWhisper)
END_SINK_MAP()
3:進一步實現這個回調函數.
4:把事件接收器連接到數據源