>>从MSDN翻译
___________________
当一个侦测到某些事情发生后,它能够通知她的客户端。例如,一个“证券行情自动记录收报机”程序发现一种证券价格发生了变化,它能够把这个变化通知给所有的客户端。这个通知进程是通过事件来触发的。
对于COM,服务对象可以使用COM事件来触发不带任何通知信息的事件。对象也可以使用可连接对象,在客户上实现输出接口,对象使用这些出接口来与客户进行通信。COM可连接对象的输出接口是由客户程序来实现,客户利用一种被称为接收器(sink)的对象来实现这些接口,并把接口指针告诉对象。因此对象可以通过这和客户保证定向的通信了(相当于建了一个专用通道,来保证对象与客户之间的通信)。输入接口在对象上实现,接受来自
对象的外部客户的调用;输出接口在客户的接收器(sink)上实现接受来自对象的调用。对象定义他要使用的接口,客户来实现他。
对象提供他的入接口,并且实现他们。客户通过对象IUnknown::QueryInterface方法来获得对象的入接口,如果对象支持客户请求的操作,则执行。 输出接口也在对象上定义,但是他确是在客户上创建的接收器对象上实现。对象接收器对象输出接口上的方法,通知客户对象上所作的改变,触发客户上的事件。
可连接对象为对象到客户的通信提供了一个通用的机制。任何愿意暴露事件或者任何种类的通知都可以使用这一技术。另外,COM还提供了很多专用的接收器用于通知客户感兴趣的指定的事件。例如 IAdviseSink 接口 可以用于通知客户对象的数据和视图的变化。
Architecture of Connectable Objects
可连接对象仅仅是可连接对象结构的一部分,这项技术包括了以下几个元素:
可连接对象 ——实现了IConnectionPointContainer 接口;创建至少一个连接点对象;为客户定义一个输出接口。
客户 ——通过查询对象IConnectionPointContainer接口决定对象是否是可连接对象;创建一个接收器对象,实现由可连接对象定义的输出接口。
接收器对象 ——实现输出接口; 用来为可连接对象建立一个连接。
连接点对象 ——实现 IConnectionPoint 接口 来管理同客户接收器的连接。
下图表示了客户、可连接对象、接收器对象、连接点对象之间的关系:
在上图第3步,连接点对象调用接收器接口的方法之前,它必须通过QueryInterface()来得到指定的接口。或者接口的指针在第2步时已通过Advise()方法获得。
两个枚举对象也包含在这个结构之中,尽管他没有在事例中体现出来。一个是通过IConnectionPointContainer 接口的方法创建,用来枚举可连接对象的连接点。另一个通过IConnectionPoint接口的方法创建,用来枚举指定的连接点的当前已建立连接。一个连接点能够支持多个连接接收器接口,每次在此接口上调用一个方法时,他通过迭代一个装有所有连接点的列表来获取指定的连接接收器接口的指针。这个过程就叫做列集。
当使用可连接对象时,弄清楚 可连接对象、每个连接点、每个接收器 和all enumerators are separate objects with separate IUnknown implementations, separate reference counts, and separate lifetimes. 客户使用这些对象后总是有责任释放他自己的引用计数。
注意:一个可连接对象可以支持多个客户,也能够支持来自一个客户的多个接收器。
同样,一个接收器也能够来接多个可连接对象。
在客户与可连接对象之间依照下列步骤建立一个连接:
1. 客户通过查询对象的IConnectionPointContainer 决定对象是否是可连接
的。如果 返回成功,客户用一个指针指向可连接对象的
IConnectionPointContainer 接口。
使可连接对象的引用计数增加。如果对象是不可连接的,它就不支持输出接口。
2. 如果对象是可连接的,客户就试着获取可连接对象的一个可连接点的
IConnectionPoint 接口的指针。有两种方法来获取这个指针,他们是
IConnectionPointContainer::FindConnectionPoint 和
IConnectionPointContainer::EnumConnectionPoints. 如果使用后者,还要
先进行一些其他步骤。(请参见Using IConnectionPointContainer 来获取更多
信息)。
如果调用成功,那么可连接对象和客户都支持相同的输出接口。可连接对象定义他
调用他,客户实现他。接着客户就能够通过可连接对象的内的连接点进行通信了。
3. 客户调用IConnectionPoint::Advise 方法来建立接收器和可连接对象的连接点
之间的连接。调用了这个方法后,对象的连接点获得了接收器的输出指针。
4. IConnectionPoint::Advise 中的代码调用 已经通过的接口指针的
QueryInterface 方法,来请求他连接方得指定的接口。
The code inside IConnectionPoint::Advise calls QueryInterface
on the interface pointer that is passed in, asking for the specific
interface identifier to which it connects.
5. 对象通过他连接点上所持有的指针,从需要的接收器接口上调用方法。
6. 客户调用IConnectionPoint::Unadvise 终止此次连接。客户再调用IConnectionPoint::Release 释放连接的指针。连接对象也需要释放相应的指针,所以客户必须调用 IConnectionPointContainer::Release 来释放可连接对象所拥有的相应的指针。
支持可连接对象要求支持以下4个接口:
IConnectionPointContainer on the connectable object
IConnectionPoint on the connection point object
IEnumConnectionPoints on an enumerator object
IEnumConnections on an enumerator object
后两个是为The IConnectionPoint * 和CONNECTDATA 定义的标准的枚举类型.(有关枚举的更多信息请参见 IEnumXxxx )另外,可连接对象可随意支持IProvideClassInfo 和 IProvideClassInfo2 为客户输出接口的运行时支持提供了足够的信息。最终客户必须提供一个接收器输出接口的实现,该接口在可连接对象上被定义。
IConnectionPointContainer, IConnectionPoint, IProvideClassInfo, and IProvideClassInfo2 接口的定义事例:
interface IConnectionPointContainer : IUnknown
{
HRESULT EnumConnectionPoints([out] IEnumConnectionPoints
**ppEnum);
HRESULT FindConnectionPoint([in] REFIID riid ,
[out] IConnectionPoint **ppCP);
}
interface IConnectionPoint : IUnknown
{
HRESULT GetConnectionInterface([out] IID *pIID);
HRESULT GetConnectionPointContainer([out]
IConnectionPointContainer **ppCPC);
HRESULT Advise([in] IUnknown *pUnk, [out] DWORD *pdwCookie);
HRESULT Unadvise([in] DWORD dwCookie);
HRESULT EnumConnections([out] IEnumConnections **ppEnum);
}
interface IProvideClassInfo : IUnknown
{
HRESULT GetClassInfo([out] ITypeInfo **ppTI);
}
interface IProvideClassInfo2 : IProvideClassInfo
{
HRESULT GetGUID( );
}