The Event Class

A publisher object fires an event at COM+ (to be delivered to the subscribers) using an event class . The event class is a COM+ provided implementation of the sink interfaces the publisher can fire the events at. The implementation is synthesized by COM+, based on a type library you provide. This library contains the interface definitions and stipulates which CoClass implements them. COM+ uses the same CoClass definition for its implementation of the event classes. To publish an event, the publisher first CoCreates the event class (the publisher has to know the event class CLSID) and then fires the events at its interfaces.

For example, suppose an object wants to fire events at the sink interface IMySink, using an event class called MyEventClass. IMySink is defined as:

interface IMySink : IUnknown
{
   HRESULT OnEvent1(  );
   HRESULT OnEvent2(  );
};

The publisher code looks like:

HRESULT hres = S_OK;

IMySink* pMySink = NULL;

hres =: =:CoCreateInstance(CLSID_MyEventClass,NULL,CLSCTX_ALL,IID_IMySink,
                           (void**)&pMySink);
ASSERT(SUCCEEDED(hres));

hres = pMyEvent->OnEvent1(  );
ASSERT(hres == S_OK);

pMyEvent->Release(  );

Compare the simplicity on the publisher side to classic COM connection points—the publisher does not have to manage lists of subscribers. All the publisher has to do is create an event class and fire the event on it.

Figure 9-2 illustrates the interaction between the publisher, the event class, COM+, and the subscribers. The client creates the event class (Step 1) and fires the event at it (Step 2). When the publisher is finished with the event class, it can either release it or cache the event class interface pointer for the sake of performance, to be used the next time the publisher wants to publish events.

The COM+ event system at work

Figure 9-2.  The COM+ event system at work

The COM+ implementation of the event class interfaces goes through the list of subscribers on that event class (Step 3) and publishes the events to them. COM+ maintains a list of subscriptions for every event class. The subscriptions can be interface pointers to existing objects (called transient subscriptions) or CLSID for a class (called persistent subscriptions).

In the case of a persistent subscription, COM+ creates an object of the type specified by the CLSID (Step 4), calls the appropriate sink method on the object (Step 5), and releases the object. In the case of a transient subscription, COM+ simply calls the appropriate sink method on the object (Step 5).

It is interesting to note that firing the event is by default serial and synchronous—that is, the subscribers are called by default one after the other (serial), and control returns to the publisher object only after all the subscribers are notified (synchronous).

Adding an Event Class

You can add an event class to the Component Services Explorer by using the Component Install Wizard. Bring up the wizard for installing a new component to your application and select Install new event class(es) (see Figure 9-3).

The Component Install Wizard is used to add a new event class

Figure 9-3.  The Component Install Wizard is used to add a new event class

The rest of the steps in the wizard are the same as when adding a new COM+ component. When you point the wizard at a DLL containing a type library with sink interface and event CoClass definitions (more about those in a minute), under-the-hood COM+ synthesizes its own implementation of the interfaces and installs the synthesized components instead of yours.

After installing the event class in the Component Services Explorer, the only way to detect that it is not a user-implemented COM+ component is to inspect its component properties page on the Advanced tab. The Advanced tab of an event class contains the Loosely Coupled Event (LCE) group (see Figure 9-4).

The LCE group configures event class-specific settings

Figure 9-4. The LCE group configures event class-specific settings

You can add an event class component to any COM+ application, be it a library or a server application.

Supplying the Event Class Definition

For COM+ to implement an event class for you, you have to provide COM+ with the sink interfaces definitions, the event class CLSID, and the interface each event class supports. You provide this information in the form of a type library. The type library has to be embedded as a resource in a DLL. The Component Install Wizard knows how to read the type library from the DLL and detect the CoClass definitions inside.

For every CoClass in the type library, COM+ tries to generate an event class and add it to your application as a component. COM+ synthesizes implementation only to interfaces that are part of the event class CoClass definition in the type library.

For example, to define the event class MyEventClass that supports the sink interface IMySink (shown earlier), your IDL file should look like this:

[
   uuid(0A9B9E44-E456-4153-9FC8-5D72234B7C82),
   version(1.0),
   helpstring("Event Class 1.0 Type Library")
]
library EVENTCLASSLib
{
   importlib("stdole32.tlb");
   importlib("stdole2.tlb");
   importlib("SubApp.tlb");//The subscribers' TLB

   [
      uuid(5CAF8E95-3FEF-40F1-94C3-3F408240D53B),
      helpstring("MyEventClass Class")
   ]
   coclass MyEventClass
   {  
      interface IMySink;
   };
};

To avoid repeating the definition of the sink interfaces in both the subscriber application and the event class type library, the event class IDL file should import the sink interface (IMySink) definitions from the type library of the subscribers. This is what the line importlib("SubApp.tlb");was used for in the previous example.

The easiest way to generate a type library is to have the Visual Studio ATL create one for you. The default behavior in ATL is to embed the type library in the DLL, since the ATL Application Wizard adds a reference to the type library in the project RC file.

I strongly recommend that you put only event classes in the event class DLL. Do not put event classes in the same type library with regular CoClasses; such a mix confuses the Install Wizard—the Wizard will install all components as event classes. This installation has potentially catastrophic results, since it may corrupt an existing installation of the regular components. However, as you have already seen in Chapter 1, you can map more than one DLL to the same COM+ application—you can put your event class and other components in the same application.

When you supply the event class, COM+ tries to register it. You are responsible for providing proper registration code in the DLL for all components contained in the DLL. Again, the easiest way is to use ATL to generate a skeleton implementation of the event class for you. Simply have the ATL Object Wizard insert new components into the event classes DLL. Since the implementation of these event classes is never called, it is a bug if anybody ever uses them. This would usually happen as a result of not installing the event class in the COM+ Catalog and only building and registering it as a normal COM object. I therefore suggest that you provide default behavior to the ATL code-assert on every method call. See Example 9-1.

Example 9-1. Skeleton implementation of the event class

class CMyEventClass : 
   public CComObjectRootEx<CComMultiThreadModel>,
   public CComCoClass<CMyEventClass,&CLSID_MyEventClass>,
   public IMySink 
{
public:
   CMyEventClass(  ){};
   DECLARE_REGISTRY_RESOURCEID(IDR_MYEVENTCLASS)
   DECLARE_PROTECT_FINAL_CONSTRUCT(  )

   BEGIN_COM_MAP(CMyEventClass)
     COM_INTERFACE_ENTRY(IMySink)
   END_COM_MAP(  )

// IMySink
public:
   STDMETHOD(OnEvent1)(  ){ATLASSERT(0);return E_NOTIMPL;};
   STDMETHOD(OnEvent2)(  ){ATLASSERT(0);return E_NOTIMPL;};
};

Event Class Interface Design Guidelines

The sink interface can be a custom interface or an automation-compliant interface. However, the methods of a sink interface can contain only input parameters. [out] or [in,out] parameters are not allowed. Since COM+ seeks to decouple the publisher from the subscribers, there is no way for a subscriber to return information back to the publisher—the call on the subscriber interface returns to COM+, not to the publisher.

From the publisher’s perspective, it only fires an event on one object—the event class.

COM+ uses type library marshaling to marshal the call on the sink interface from the event class to the subscribers. Interfaces that use type library marshaling must comply with the following requirements:

  • All the methods must return HRESULT.

  • The methods do not use certain IDL attributes such as [size_is] and [length_is]. See the MSDN documentation for the exact specification of typelib-compliant IDL attributes.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset