Page #109 (Tightly Coupled Events (TCEs))

< BACK  NEXT >
[oR]

Loosely Coupled Events (LCEs)

While the TCE techniques are quite useful, they have some drawbacks in certain scenarios:

  • The lifetime of the publisher and the subscriber is tightly coupled. The subscriber has to be running and connected to the publisher to receive the events. This is not usually a problem for subscribers such as ActiveX controls, which has no reason to receive events outside the lifetime of their containers. However, in an enterprise system, forcing the subscriber to run at all times doesn t scale well.

  • The TCE techniques were developed without regard to distributed environments. A technique such as connection points is not very efficient in terms of the number of round trips required to establish and break down the connection. More importantly, there is no support in place to guarantee a persistent connection, as the only binding between the entities is the exchanged interface pointer.

  • Under TCE, there is no mechanism in place to filter events. For instance, in the earlier stock price events example, a subscriber ends up getting the price changes for all the stocks even though the subscriber is interested in watching only a selected list of stocks.

One way to address these problems is to bind the two entities at a higher level of abstraction than the interface pointer. Using this higher level binding information, the publisher can connect to the subscriber at the time the event is fired. The lifetime of the publisher and the subscriber are no longer tightly coupled. If the subscriber is not running at the time of firing the event, the mechanism will automatically start the subscriber. Likewise, a subscriber can still subscribe to an event even if there is no publisher running. Such a system is referred to as a loosely coupled event (LCE) system.

A simple way to implement an LCE system is to have the publisher maintain an external database of subscriber CLSIDs. The publisher also documents the mechanism by which a subscriber can add its CLSID to the database. When it is time to fire an event, the publisher goes through each CLSID in the database, instantiates an object of the class, and calls a method on that object. The database schema can easily be extended to let the subscriber define their filtering criteria.

This design approach has two snags:

  • Each publisher will need to develop and maintain the code to manage the subscription database.

  • As each publisher defines its own subscription process, there is no standard process to subscribe to an event and/or define the filters on the event.

COM+ defines a standard mechanism to publish and subscribe an LCE event that overcomes both of the above-mentioned problems. This mechanism is called COM+ Events.

COM+ Events Architecture

The COM+ Events architecture provides built-in logic for managing and maintaining subscription database. In addition, it defines a standard format for the publisher to advertise information on the events it can fire and for the subscriber to locate and subscribe to the event.

The COM+ Events architecture is shown in Figure 10.3. Under this architecture, the publisher and the subscriber are decoupled by means of an intermediary object called the event class. An event class is a COM+ component that contains the interfaces and methods used by a publisher to fire events. An event is defined as a single call to one of the interface methods from the event class. A subscriber implements the interfaces and the methods of the event class it wants to receive events from while the publisher calls a specific interface method to fire an event.

Figure 10.3. COM+ Events architecture.
graphics/10fig03.gif

Event classes are stored in the COM+ catalog, typically placed there by the publishers. The part of the catalog that stores event information is referred to as the event store.

Matching and connecting publishers and subscribers is done by a COM+ service called the COM+ Event service.

To indicate its desire to receive events, the subscriber registers a subscription with the COM+ event service. A subscription is a data structure that provides the event service with information about the subscriber so that it can connect to the subscriber to deliver an event. Among other things, it specifies the event class and the specific interface or method within the event class the subscriber wants to receive calls from.

Subscriptions can be registered either by the subscribers themselves or by some administrative programs. When registered, the subscription information is stored in the COM+ catalog.

A subscription comes in two flavors: persistent and transient. Persistent subscriptions survive a system restart. They exist independently of the lifetime of the subscriber object. A transient subscription requests that event calls be made to a specific running instance of a subscriber. Even though a transient subscription is stored in the COM+ catalog, the subscription does not survive a system shutdown.

When a publisher wants to fire an event, it creates an object of the desired event class (using CoCreateInstance in C++ or CreateObject in VB). This object, known as the event object, is synthesized by the event service. It contains the event service s implementation of the requested interface. The publisher then calls the event method it wants to fire an event on. When the method is invoked, the synthesized implementation of the method looks in the COM+ catalog and obtains a list of all the subscribers that have registered subscriptions to that interface method. It then instantiates the subscriber and calls the specified event method. The instantiation logic could create either the subscriber object directly or use monikers or queued components, as we will see later.

From the publisher s perspective, firing an event is as simple as calling the appropriate method on the event class. It is the responsibility of the event service to deliver the event to the subscribers. For a persistent subscription, the event service creates the subscriber object, invokes the appropriate method on the subscriber object, and releases the object. For a transient subscription, the subscriber object already exists, eliminating the need to create and destroy objects on each event call.

As there could be more than one subscriber to a specific event, the event service places some restrictions on the interface method that receives the event the method cannot have any output parameters. It can only return an HRESULT to indicate success or failure. Not only does this simplify the publisher s logic, it also makes it possible to queue an event if a subscriber is not running when an event is fired, as we will see later.

Queued or non-queued, firing an event is always synchronous with respect to the event object. When a publisher fires an event, all the non-queued subscriptions are called immediately and the queued subscriptions are recorded for later playback. When the event call returns from all the subscribers, the event service consolidates the return codes (HRESULTs) from each subscriber and returns one aggregated HRESULT value to the publisher. Table 10.1 lists some possible return codes to the publisher.

Table 10.1. Event Method Return Codes

Return Code

Description

S_OK

All of the subscribers were successfully invoked.

EVENT_S_SOME_SUBSCRIBERS_FAILED

Some subscribers could not be invoked or some subscribers returned a failed HRESULT code.

EVENT_E_ALL_SUBSCRIBERS_FAILED

None of the subscribers could be invoked.

EVENT_E_NOSUBSCRIBERS

There are no subscribers for the fired event.

If one or more subscribers fail, there is currently no simple way to identify the subscribers or the reason for failure. This, in general, is not a problem as an LCE publisher rarely cares about subscribers identities. If the publisher does need to get this information, it would have to implement a publisher filter, as we will see later.

The event service does not currently provide any mechanism for specifying the order in which the subscribers receive an event. The default protocol is to deliver the event to one subscriber at a time. However, this default behavior can be changed by instructing the event service to fire an event in parallel, that is, to deliver an event to multiple subscribers concurrently. [3] This reduces the average delivery time of event notifications.

[3] Currently, there is no multicasting mechanism for event delivery. The event service just uses a thread pool to reach multiple subscribers concurrently.

By default, the in-process activation of subscribers is prohibited for security reasons. However, this behavior can be changed by setting an option on the event class.

With this background, let s now look at the implementation specifics of the publisher and the subscriber.

A Simple Event Example

We will continue with the theme of monitoring changes in stock prices. Following is the interface definition for the event class:

 interface IMyStockPriceEvent : IDispatch  {   HRESULT NewQuote([in] BSTR bsSymbol, [in] double dValue);  }; 

The publisher application will simulate stock price changes by calling the NewQuote method. The subscriber application will pop up a message box each time it receives an event.

Registering an Event Class

An event class is a COM+ configured component that requires the following:

  1. A GUID to represent the event class. This GUID is referred to as EventCLSID. The EventCLSID is specified as the CLSID of the component.

  2. A readable unique identifier, called EventClassName, for the event class. The EventClassName is specified as the PROGID of the component.

  3. A type library. In order to synthesize the event object and provide marshaling logic for the event class interfaces, the event service requires that the interfaces contained in the event class be provided in a type library.

  4. An optional publication attribute called the PublisherID. A subscriber can choose to use PublisherID instead of EventCLSID to identify an event class.

graphics/01icon02.gif

When defining an event class interface, you have to use automation-compliant method parameters. Do not use MIDL attributes such as size_is and length_is; they do not work with type library marshaling.


Being a COM+ configurable component, the event class requires a self-registering DLL that exports DllRegisterServer (to register the component) and DllUnregisterServer (to unregister the component).

The easiest way to create an event class is to use the ATL COM AppWizard to generate a dummy component. The coclass section in the IDL file should indicate the desired interfaces the event class intends to support. This is illustrated in the following IDL code fragment. It is taken from the ATL AppWizard generated project that I created to define our stock price event class.

 library STOCKPRICELib  {   ...    coclass MyStockPriceEvent    {     [default] interface IMyStockPriceEvent;    };  } 

Note that the internal structure of the ATL COM AppWizard requires us to provide the implementation of the interface methods. However, the methods on this component are never called by the event service (the service provides its own implementation of the event class). Therefore, the method implementation can just be a stub that simply returns S_OK.

To register the event class, first create a new COM+ application using the Component Services snap-in. Add a new component to this application. This brings up the component-install dialog box shown in Figure 10.4.

Figure 10.4. Installing an event class component.
graphics/10fig04.gif

Click on the Install new event class button. Enter the path to the event class component. This brings you to the dialog box shown in Figure 10.5.

Figure 10.5. Installing event class files.
graphics/10fig05.gif
Figure 10.5. Installing event class files.
graphics/10fig05.gif

Click Next followed by Finish.

You have just finished adding an event class to the COM+ catalog!

graphics/01icon01.gif

An event class could also be installed using the COMAdmin administrative object. COMAdmin objects are covered in Chapter 12. You can find a VBScript program on the CD to install the event class using the COMAdmin object. For those interested, you have to call the InstallEventClass method on the ICOMAdminCatalog interface, which is what the Component Services snap-in does internally.


When viewed from the Component Services snap-in, the event class component doesn t look any different from a typical component. The only difference is that the Advanced tab of the component s property sheet contains a few more entries, as shown in Figure 10.6. For our example, I have defined the identification of a publisher as My Stock Price Publisher. However, defining this field is not mandatory.

Figure 10.6. Event class properties.
graphics/10fig06.gif

We are done registering the event class. Now let s see how we can implement a subscriber.

Subscribing for an Event

For our demonstration, we will look at creating a persistent subscription. Transient subscriptions will be covered later.

A persistent subscriber is a regular COM+ configured component. As such, it can be developed using the ATL AppWizard. The important thing to keep in mind is that the component must support the event interface(s) on which it is interested in receiving events as shown in the following C++ class definition:

 class CMyStockEventSubscriber :    public IDispatchImpl<IMyStockPriceEvent,      &IID_IMyStockPriceEvent, &LIBID_STOCKPRICELib>,    ...  {   ...  // IMyStockPriceEvent  public:    STDMETHOD(NewQuote)(BSTR bsSymbol, double dValue);  }; 

Do not forget to refer to the event interface within the coclass section of the IDL file for the subscriber. Otherwise, the event interface will not be available for subscription.

graphics/01icon02.gif

Always refer to the interested event interfaces in the subscriber s IDL file.


Implementing an event interface is all it takes to create a subscriber. This is as simple as it gets. Compare this to the connection point implementation that required a boatload of code to set up the connection.

Our demonstration example will just bring up a message box when an event gets delivered via NewQuote method as shown in the following code snippet:

 STDMETHODIMP CMyStockEventSubscriber::NewQuote(BSTR bsSymbol,    double dValue)  {   TCHAR buf[100];    _stprintf(buf, _T("%S %lf"), bsSymbol, dValue);    ::MessageBox(NULL, buf, _T("Stock Price"), MB_OK);    return S_OK;  } 

Now, compile and link the subscriber code.

Next, we need to add the subscriber to the COM+ catalog. For that, we first need to install the subscriber as a regular COM+ configured component. We will create a new application from the Component Services snap-in and add the component in the usual manner to this application.

Note that the subscriber need not be added to a separate application. You can add the subscriber to the same application as that of the event class component, if desired.

Once a component is added, you will notice that each component shown in the Component Services snap-in contains two folders one is our all-too-familiar interfaces folder, and the second one is a Subscriptions folder. This is shown in Figure 10.7.

Figure 10.7. Subscription folder.
graphics/10fig07.gif

Select Add New Subscription from the context menu of the subscription folder. This brings up a subscription wizard that lists all the interfaces that the component claims to support (from the coclass section of the IDL file). This is shown in Figure 10.8.

Figure 10.8. Available interfaces for subscription.
graphics/10fig08.gif

You can subscribe to a single method or to all the methods on an interface. If you want to receive events on more than one method, but not every method, you have to add a separate subscription for each desired method.

Once you choose an interface or a specific method on the interface and click on the Next button, the subscription wizard searches through the COM+ catalog for registered event classes that support the specified interface and offers you a choice to subscribe, as shown in Figure 10.9.

Figure 10.9. Event class choices for subscription.
graphics/10fig09.gif

You can select a specific event class (recall that an event class is typically tied to a publisher). Alternatively, if you don t care for a specific event class, you can select all the event classes that implement the specified interface.

Selecting an event class brings up the next dialog box that requires you to specify a name for the subscription, as shown in Figure 10.10.

Figure 10.10. Subscription properties.
graphics/10fig10.gif

A subscription may be enabled or disabled. If disabled, a subscription does not receive any event notification. For our demonstration, we select the option to enable the subscription from the subscription wizard dialog box. Alternatively, we could have done this later from the property sheet for the subscription.

Click the Next button followed by the Finish button. At this point, the wizard adds the subscription information to the subscription list in the catalog.

Similar to the event class installation, a subscription can also be installed programmatically using the COMAdmin objects. You can find a VBScript file on the CD that demonstrates this.

Firing an Event

A publisher need not be a COM component. It can be any application that is interested in firing an event. When the publisher wants to fire an event, it simply creates an object of the event class and calls the appropriate method on it. The following VBScript code fragment shows how to fire our stock price change event:

 Set stockPriceEvent=CreateObject("StockPrice.MyStockPriceEvent")  stockPriceEvent.NewQuote "MSFT", 100.0  msgbox "Done" 

Firing an event is as simple as this. There s no need to maintain and manage a list of subscribers; COM+ does the work.

Transient Subscriptions

We know that a persistent subscriber can exist independently of the lifetime of the publisher. If the subscriber is not running at the time the event is fired, the COM+ event service forces it to start. Essentially, the service manages the lifetime of the subscriber.

There are cases, however, where the lifetime of a subscriber object is controlled externally. For example, the lifetime of an ActiveX control is tied to the lifetime of its container. If the container application quits, the ActiveX control object dies. Such a subscriber would only care about getting updates during its externally controlled lifetime. This is where transient subscriptions come into play.

A transient subscription requests that the event calls be delivered to a specific existing subscriber object. Transient subscriptions do not survive system shutdown, although they are stored in the catalog.

Developing the subscriber for transient subscription is similar to that of a persistent subscription in that the subscriber has to support the interested event interface. The implementation follows:

 class CMyTransientEvent : public IMyStockPriceEvent  { public:    ...  // IMyStockPriceEvent  public:    STDMETHOD(NewQuote)(BSTR bsSymbol, double dValue);  };  // Create the object  CComPtr<CComObject<CMyTransientEvent> > spEvent;  HRESULT hr =  CComObject<CMyTransientEvent>::CreateInstance(&spEvent);  _ASSERT(SUCCEEDED(hr));  spEvent->InternalAddRef(); 

The Component Services snap-in doesn t have any provision to set up a transient subscription. One has to use the COMAdmin objects to do so.

The first step is to obtain the list of transient subscriptions from the catalog as shown here:

 COMAdmin::ICOMAdminCatalogPtr spCat(   __uuidof(COMAdmin::COMAdminCatalog));  COMAdmin::ICatalogCollectionPtr spColl =    spCat->GetCollection("TransientSubscriptions"); 

The second step is to add the transient subscription to the TransientSubscriptions collection object, represented by the ICatalogCollection interface.

Adding a transient subscription requires the following items:

  1. A user-defined name for the transient subscription.

  2. The IID of the interface it wishes to receive events on.

  3. An interface pointer to the subscriber object.

  4. Optionally, the EventCLSID or the PublisherID of the publisher s event class.

The following code fragment shows how to add a transient subscription:

 IDispatchPtr spDisp = spColl->Add();  COMAdmin::ICatalogObjectPtr spCatObject = spDisp;  spCatObject->Value["Name"] = bsSubscriptionName;  spCatObject->Value["InterfaceID"] =    "{A9E6D819-1891-462D-B32C-ED4AFD61B08B}";  // spCatObject->Value["EventCLSID"] =  //  "{1F6F353D-5738-4C05-9DA1-A64E19370A0E}";      // optional  // spCatObject->Value["PublisherID"] =  //  "My Stock Price Publisher";                    // optional  spCatObject->Value["SubscriberInterface"] =    static_cast<IUnknown*>(spEvent);  spColl->SaveChanges(); 

Notice the call to the SaveChanges method. Without this call, no changes are saved in the catalog.

The object is now set to receive events!

To stop receiving events, the transient subscription has to be programmatically removed from the catalog, as shown here. The code is included on the CD.

 spColl = spCat->GetCollection("TransientSubscriptions");  spColl->Populate();  long lIndex = GetIndexInCollection(spColl, bsSubscriptionName);  spColl->Remove(lIndex);  spColl->SaveChanges(); 

In a persistent subscription, a subscriber object is repeatedly constructed and destructed each time an event is fired. In a transient subscription, however, the subscriber object is constructed just once. This makes a transient subscription more efficient than a persistent subscription.

Events and Queued Components

LCE in COM+ greatly simplifies publisher coding. To fire an event, the publisher just creates a single event class object and calls an appropriate method on it. Keeping track of the subscribers and delivering events to the subscribers is left to the COM+ event service.

There are two problems with this mechanism:

  1. Consider the case where the event class is installed on a remote machine. If the network is down, the publisher will not be able to create an event object, and therefore cannot fire any events. However, some subscribers may be interested in receiving the events when the network eventually comes back up.

  2. In an enterprise system, there could be many subscribers spread across many machines. When an event is fired, there is a possibility that either a subscriber is not running or a subscriber could not be reached because the network is down.

In both these cases, the problem stems from the fact that the normal event firing or event delivery mechanism uses synchronous DCOM, and synchronous DCOM calls fail if the remote object cannot be reached.

If you haven t skipped Chapter 9, you know that the solution lies in using Queued Components.

Fortunately, COM+ events were designed to work closely with Queued Components.

The first problem can be easily solved by making the event class itself a Queued Component. You do this by marking the event class application as queued and listen, as well as marking the event interface as queued (see Chapter 9).

Of course, in order to connect to the Queued Component s recorder, the publisher needs to instantiate the object using the queue moniker syntax. The following code fragment illustrates the technique:

 Set stockPriceEvent = GetObject( _    "queue:ComputerName=PVDEV/new:StockPrice.MyStockPriceEvent")  stockPriceEvent.NewQuote "MSFT", 100.0  stockPriceEvent = NULL  msgbox "Done" 

The second problem can also be solved similarly. In order to queue the publisher s event and replay it to the subscriber later, the specific subscription should be marked as queued. This can be done from the Options tab on the property sheet of the subscription, as shown in Figure 10.11.

Figure 10.11. Marking a subscription as queued.
graphics/10fig11.gif

Once the subscriber is marked as queued, the event object will create a queued connection to the subscriber. However, to create a queued connection, marking just the subscription as queued is not enough. You need to mark the subscriber s application as queued and listen and the event interface as queued. For our demonstration sample, interface IMyStockPriceEvent should be marked as queued.

In summary, to queue a COM+ event to a subscriber:

  • the application the subscriber belongs to should be marked as queued and listen

  • the event interface on the subscriber should be marked as queued

  • the subscription itself should be marked as queued

Note that firing an event is always synchronous with respect to the event object. When an event method returns, calls to all Queued Component subscribers have already been recorded. If it is a persistent subscriber, the message will be enqueued for transmission (recall from Chapter 9 that a recorded message is not enqueued until the object is released). For a transient subscriber, as the object is released only after its subscription is removed, a message is not enqueued while the subscription is active.

There is an order of delivery implication to consider when using Queued Components. Within the lifetime of a single subscriber object, all the event calls are recorded as one message. Therefore, all calls will be replayed in the order in which they were made. However, if the publisher fires on more than one event object from the same application, the order cannot be guaranteed. This is because the COM+ QC listener service uses multiple threads to receive MSMQ messages. Essentially, anything in the OS can affect the scheduling of different threads. It is entirely possible that the second message may get dequeued before the first message.

A similar problem exists when the subscriber is marked as a Queued Component. Multiple event notifications will be queued as multiple messages. The COM+ listener service may pick the second message before the first one. If the subscriber is dependent on the order of the event notifications, queuing should not be used.

Events and Filtering

So far we have learned that it is reasonably easy to set up an LCE-based application under COM+. A publisher fires an event and the subscriber receives it.

There are situations, however, when a subscriber might not care for all the events that a publisher fires. For example, most investors are probably interested in a handful of stocks, and would only want the price changes for these stocks.

A subscriber can certainly write code to filter out uninteresting stocks. However, it would be nice if this task could be handled administratively. Some reasons for this follow:

  • Less code for the subscriber to write and maintain.

  • A subscriber object is created and destroyed each time an event is fired. Also, the event, even if it is an uninteresting one, gets delivered to the subscriber, taking up unnecessary CPU time as well as network bandwidth.

Likewise, there are times when a publisher wants to selectively pass an event to the subscriber. Some examples of such cases are:

  • The subscriber may not be licensed to receive the events.

  • The subscriber may indicate to the publisher some other event filtering criteria.

The COM+ event service offers a simple subscriber-side filtering of events and a not-so-simple publisher-side filtering of events. Let s take a look.

Subscriber Parameter Filtering

The COM+ event service provides a simple mechanism to define filters on event method parameters. The type of filtering used is on a per-method per-subscription basis. The filter can be specified from the Options tab on the property sheet of the subscription, as shown in Figure 10.12.

Figure 10.12. Filtering criteria for a subscription.
graphics/10fig12.gif

The filter criteria string supports relational operators (=, ==, !, !=, ~, ~=, <>), nested parentheses, and logical keywords AND, OR, or NOT.

Filtering criteria can also be specified programmatically using COMAdmin objects. For those interested, it is the FilterCriteria property on the subscription collection.

When an event is fired, the event service checks the filtering criteria for each subscription and delivers the event only if the filter matches the current event values.

graphics/01icon02.gif

Beware of entering a syntactically incorrect filter. The Component Services snap-in does not complain about it. However, the event system logs an error entry into the Windows event log. Be sure to check the event log while testing your filters.


Publisher Filtering

Filtering can also be done at the publisher level instead of the event service level. Not only is this more efficient, but it also provides the following advantages to the publisher:

  • The publisher can control the order of firing the events. Perhaps some subscribers might have paid extra for priority notification.

  • The publisher can check the returns status from each individual subscriber.

  • The publisher can check if the subscriber is licensed to receive event notifications.

The publisher can exercise fine-grained control over the event firing process by adding the necessary logic in the publisher code itself. However, when used this way, the filter behavior cannot be changed without rebuilding the entire publisher program. Moreover, it won t help you control a publisher for which you have no source code.

Fortunately, the COM+ event system provides a mechanism by which a publisher filter can be defined as a separate COM+ configured component. When installing an event class, you can specify which publisher filter to use by setting the event class PublisherFIlterCLSID property in the COM+ catalog. Unfortunately, the Component Services snap-in doesn t let you specify the filter component; you need to write your own administrative program to do this. You can find an example on the CD.

This filter class needs to support a COM+ provided standard interface, IMultiInterfacePublisherFilter. [4] Table 10.2 lists its methods.

[4] This interface replaces an earlier interface, IPublisherFilter. The latter interface still exists but its use is deprecated.

Table 10.2. IMultiInterfacePublisherFIlter Methods

Method

Description

Initialize

Called by the event system when the event object is first created.

PrepareToFire

Called by the event system when an event needs to be fired.

Besides supporting IMultiInterfacePublisherFilter, the filter class should also support the event interfaces supported by the event class, as illustrated in the following code-fragment:

 class CMyFilterImpl :    public IMultiInterfacePublisherFilter,    public IMyStockPriceEvent,    ...  {   ...  // IMyStockPriceEvent    STDMETHOD(NewQuote)(BSTR bsSymbol, double dValue);  // IMultiInterfacePublisherFilter methods    STDMETHOD(Initialize)(IMultiInterfaceEventControl *pMIEC);  STDMETHOD(PrepareToFire)(REFIID riid, BSTR methodname,    IFiringControl* pFC);  private:    CComPtr<IEventObjectCollection> m_spColl;    CComPtr<IFiringControl> m_spFC;  }; 

When the publisher creates the event object, the event system reads the CLSID of the filter specified for the event class and creates an object of the filter class. If the filter object cannot be created, the creation of the event object fails.

After creating the filter object, the event system calls the IMultiInterfacePublisherFilter::Initialize method, passing in a pointer to IMultiInterfaceEventControl. This interface supports a method, GetSubscriptions, that returns a subscription list for a given interface and a given method. The filter should hold on to this collection, as shown here:

 STDMETHODIMP CMyFilterImpl::Initialize( IMultiInterfaceEventControl *pMIEC)  {   _ASSERT (ISNULL(m_spColl));    int nErr = 0;    HRESULT hr = pMIEC->GetSubscriptions(     __uuidof(IMyStockPriceEvent),      g_bsMETHODNAME,      0,      &nErr,      &m_spColl);    return hr;  } 

Note that our example event class has just one interface with just one method, making us cache just one collection. In general, you may have to cache many collections. For a more complex example, check the EventMonitor sample program on the platform SDK.

When the publisher calls a method on the event interface, the event system calls the filter s IMultiInterfacePublisherFilter::PrepareToFire method. The first two parameters identify the event interface and the event method. The last parameter is a pointer to the IFiringControl interface. This interface has a method, FireSubscription, that we need to use later for delivering events to the subscribers.

 STDMETHODIMP CMyFilterImpl::PrepareToFire(REFIID riid,    BSTR methodname, IFiringControl* pFC)  {   if (NULL == pFC)      return E_INVALIDARG;    _ASSERT (__uuidof(IMyStockPriceEvent) == riid);    _ASSERT (!wcsicmp(methodname, g_bsMETHODNAME));    m_spFC = pFC;    return S_OK;  } 

The event system now invokes the event method on the filter object. At this point, the filter object can choose to filter event notification to some subscribers and can control the order of firing events.

For our sample, we will check to see if a subscriber is licensed to receive the events.

The event system supports a notion of specifying arbitrary name/value pairs to a subscription. These pairs are referred to as the publisher properties and can be added to a subscription from its property sheet, as shown in Figure 10.13.

Figure 10.13. Publisher properties for a subscription.
graphics/10fig13.gif

For our sample program, the filter expects each subscription to have a property called MyLicenseKey. The magic value for the license is 1234. The following code shows the license-check logic.

 STDMETHODIMP CMyFilterImpl::NewQuote(/*[in]*/ BSTR bsSymbol,    /*[in]*/ double dValue)  {   _ASSERT (ISNOTNULL(m_spFC));    _ASSERT (ISNOTNULL(m_spColl));    CComPtr<IEnumEventObject> spEnum;    HRESULT hr = m_spColl->get_NewEnum(&spEnum);    RETURNIFFAILED(hr);    ULONG nRet = 0;    CComPtr<IUnknown> spUnk;    while(S_OK == (hr = spEnum->Next(1, &spUnk, &nRet))) {     CComPtr<IEventSubscription> spSub;      hr = spUnk->QueryInterface(&spSub);      RETURNIFFAILED(hr);      spUnk = NULL;      CComVariant v;      hr = spSub->GetPublisherProperty(CComBSTR("MyLicenseKey"),        &v);      if (FAILED(hr)) {       continue; // not a valid subscriber      }      if (wcscmp(V_BSTR(&v), L"1234")) {       continue; // not a valid subscriber      }      hr = m_spFC->FireSubscription(spSub);    }    return hr;  } 

In the above code, when the event method is invoked, the filter object goes through each subscription looking for the license key property. If the value for this property matches the magic value, the filter object tells the event system to deliver the event to the specific subscriber, using the FireSubscription method. This method invokes all the event system s standard delivery mechanism, including the parameter filtering described earlier.


< BACK  NEXT >


COM+ Programming. A Practical Guide Using Visual C++ and ATL
COM+ Programming. A Practical Guide Using Visual C++ and ATL
ISBN: 130886742
EAN: N/A
Year: 2000
Pages: 129

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net