COM Connection Point Architecture (COM Event)
In classic COM, the ability for one object to send events to another object requires four key ingredients:
- A connection point container
- A connectable object (or possibly a set of them) maintained by the container
- An source interface (aka outbound interface) defined in the server’s type library
- The client’s implementation of the source interface (aka client-side sink).
Connection Point Container
Connection point container is just a fancy name for a collection coclass that implements the standard COM interface named IConnectionPointContainer. The role of IConnectionPointContainer is to allow the client to investigate the set of connectable objects it is maintaining. The official definition of this standard COM interface can be found inside ocild.idl:
// Implemented by the connection point container.
interface IConnectionPointContainer : IUnknown
{
// Allows client to enumerate over the inner objects.
HRESULT EnumConnectionPoints(
[out] IEnumConnectionPoints ** ppEnum);
// Allows the client to ask for a connection point by name.
HRESULT FindConnectionPoint( [in] REFIID riid,
[out] IConnectionPoint ** ppCP);
}
EnumConnectionPoints can be called to discover all the connection points and their source interfaces supported by the object. This method returns an IEnumConnectionPoints interface, which provides methods to enumerate over the collection of connection points, each of which is represented as an IConnectionPoint interface.
FindConnectionPoint enables a client to ask for a specific connection point identified by an IID of a source interface. This is extremely similar to QueryInterface, but in this case a client asks what source interfaces the object calls rather than what interfaces the object implements. If successful, an IConnectionPoint interface is returned.
Connectable Object
A connectable object is a coclass that implements the members of IConnectionPoint. This interface defines a set of methods that allows
the external client (sink object) to connect and disconnect from the connectable object.
IConnectionPoint has Advise and Unadvise methods that enable clients to hook and unhook a callback interface pointer as follows:
- A client calls Advise with an IUnknown pointer to itself, then Advise passes back an integral “cookie” that uniquely identifies the connection. The callback object being passed to Advise must implement the appropriate source interface.
- When the client is finished listening to events, it can call Unadvise with the cookie value obtained from Advise to notify the connectable object.
IConnectionPoint has a few additional methods that enable enumerating of connections, obtaining the IID of a connection point’s source interface, or obtaining a connection point’s container.
Source Interface
A given connectable object is only able to make calls against a particular set of methods. Formally speaking, this set of methods is known as an source interface (outbound interface), which is defined in IDL using the [source] keyword. Each source inteface corresponds exactly to one connection point. Source interfaces are defined in the server’s type information but implemented by the client in a given sink object. Also understand that outbound interfaces are defined as dispinterfaces (by convention) to ensure that late-bound clients (such as a Web browser) can intercept the incoming events. Here is a simple IDL definition of an outbound interface:
library MYEVENTSERVERLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");
// Event interfaces are defined in the server’s IDL,
// but implemented by the client. The underscore is a
// convention that marks the interface as hidden.
[uuid(17B8B6D5-887C-46B4-9B4D-554954863CD8)]
dispinterface _ICoEventObjectEvents
{
properties:
methods:
[id(1), helpstring("method TheEvent")] HRESULT TheEvent();
};
[uuid(F94E0935-7DE1-46CC-9E3C-BFDE8998A80B)]
coclass CoEventObject
{
[default] interface ICoEventObject;
[default, source] dispinterface _ICoEventObjectEvents;
};
};
Although it is possible that a COM server may define multiple source interfaces (and therefore multiple connection points), 99.9 percent of all connection point containers define a single connectable object and a single [default, source] interface that defines all the events for a given container.
Sink Object
A sink is COM class implements the source interface. A sink object being attached to a connection point (from passing it a source interface) is considered a connection.