Everything we've seen so far regarding IDispatch and Automation, along with type information, applies to an object's incoming interface as well as its outgoing interfaces or event sets. As we saw in Chapter 3, interfaces and dispinterfaces listed for a coclass in an ODL file can have the source attribute, marking that interface as outgoing. An object with one or more outgoing interfaces supports connection points that a client can use to hook up sinks to catch events and notifications.
When an event set is a vtable interface, the object fires the event by calling Interface::<Event> through whatever pointers it has been given from its clients. When an event set is a dispinterface, however, the object has to call IDispatch::Invoke because each event sink will implement that event set through IDispatch. In this case, the object becomes a client of IDispatch and must pass the right arguments and parameters, such as DISPPARAMS and LCID. However, because the object itself defined the dispinterface, it already knows exactly which dispIDs correspond to which events as well as the proper argument types. The object, therefore, doesn't have to mess with its own type information and the event sink doesn't have to implement any function in IDispatch except Invoke and the IUnknown members. The relationship is a lot simpler, at least for the object. If a client or a controller wants to be able to handle arbitrary events from arbitrary objects, it has to browse the object's type information in order to find the names and dispIDs of each event as well as the types necessary to correctly understand and coerce the arguments that may accompany those events.
Handling arbitrary events in this manner as a client is a topic for Chapter 24, where we'll look at containers for OLE controls that will want to attach specific code to individual control events. Making calls to dispinterfaces, be it from an automation controller or an object firing events, is the topic of Chapter 15.