连接点语义
第一种说法:是一种逻辑上的反馈机制,这种机制允许对象暴露其”调用一个或者多个指定接口的能力”
第二种说法: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:把事件接收器连接到数据源