Library Zone Articles
External Articles
Byte Size

Discovery Zone Catalogue
Diary
Links
Bookstore
Interactive Zone Ask the Gurus
Discussion Groups
Newsletters
Feedback
Etc Cartoons
Humour
COMpetition
Advertising
Site Builder ASP Web Ring ASP Web Ring

Power your site with idr newswire
The Developer's Resource & Community Site
COM XML ASP Java & Misc. NEW: VS.NET
International This Week Forums Author Central Find a Job
Part of the COM+ Development Series

COM+ Basics - Creating your first COM+ Application

Down load article for all the code used

Download print article

Introduction:

The technologies surrounding the rise of the Internet are legions already. With Windows 2000, Microsoft adds its new version of COM to the fray. Located at the heart of Windows Distributed interNet Architecture (DNA), the COM+ Services provide an interesting architecture. This article will guide you trough a sample application that uses some of the services offered by the new platform. The sample simulates a Shopping Market, from the client order entries to the automatic orders handling mechanism. The article will only discuss the business logic implementation, where COM+ Services live and breathe.

Application Setup

The data layer is represented by a small database containing three small SQL tables: TblArticles, which contains the available articles for sale; TblClients, which contains the registered clients and their credit and TblOrders, which contains the clients orders.

You can setup the database easily: using the SQL Server Enterprise Manager, create a new database called Shipping. Then, use SQL Server Query Analyzer to connect to the Shipping database and run the Shipping.sql script located in the SQL folder of the application. The last step is to import the sample data: in Enterprise Manager, use the Tools\Data Transformation Services\Import Data dialog and select Text File as a Source to import both TblClients.txt and TblArticles.txt in the Shipping database.

Some simple VBScript files simulate the presentation layer; they can be found in the Script folder.

The Business Logic

Before starting with the project, we must define the functionality we need and the services we want to use on the middle tier. In the sample, we want to be able to display the articles for sale to the clients and allow them to order one or more of these. We also want to have an automated mechanism that processes orders, charges the clients and ships the requested articles.

The following COM+ Services will help us build a powerful, scalable solution:

  • COM+ object pooling, to share database connections
  • COM+ queued components, to submit orders asynchronously
  • COM+ events, to handle orders asynchronously
  • COM+ transaction support (with database rollback), to make the shipping mechanism coherent

The Shipping Project

Creating a COM+ DLL is not very different than a standard COM or MTS-based ATL project. The only thing worth mentioning is that COM+ now offers new interfaces for the management of objects states; although the old IObjectControl interface is still supported, I recommend the use of the new interfaces (more on this later). That means we don't mark the project as supporting MTS when using the wizard.

COM+ Object Pooling

The first component, Shipping.Info, is used for querying the database about users and articles for sale. The component is marked as Neutral threaded in the .rgs file to support pooling and prevent thread switches. Its interface is called IdevInfo - since there is no namespace in IDL, it is good practice to give some sort of prefix to your interface names; here Idev fits well.

By looking at the code of Info.cpp, you can see that the database connection is established in a method called Construct. COM+ services offer a simple way of passing an initialization string parameter to a component. All you have to do is have your component implements the IObjectConstruct interface and COM+ will automatically call the Construct method on component creation. Here, specifying the database connection string in the object constructor string is a fine way of making this code portable. Retrieving the constructor string through IObjectConstructString is trivial and the Construct method uses it to create the database connection and store it in a member variable.


if ( ccpIConstruct &&
     SUCCEEDED( hr = ccpIConstruct->get_ConstructString( &ccbstrDSN ) ) &&
     SUCCEEDED( hr = m_ccpIConnection.CoCreateInstance( L"ADODB.Connection" ) ) &&
     SUCCEEDED( hr = m_ccpIConnection->Open( ccbstrDSN ) ) )
  return S_OK;

The open connection can then be reused for fast database access in the get_Clients and get_Articles properties. To enable pooling (deactivation) between method calls, the Info component uses the IContextState interface instead of the old IObjectContext to set the Done bit of the context to True.


if ( SUCCEEDED( hr = CoGetObjectContext( IID_IContextState, (void**)&ccpIState ) ) &&
     SUCCEEDED( hr = ccpIState->SetDeactivateOnReturn( VARIANT_TRUE ) ) &&
     SUCCEEDED( hr = ccpIRSClients.CoCreateInstance( L"ADODB.Recordset" ) ) &&
     SUCCEEDED( hr = ccpIRSClients->Open( CComVariant(L"TblClients"), 
                                          CComVariant(m_ccpIConnection),
                                          adOpenDynamic, 
                                          adLockReadOnly, 
                                          adCmdTable ) ) &&
     SUCCEEDED( hr = ccpIRSClients.CopyTo( ppIRSClients ) ) )
  return S_OK;

The second component, Shipping.Cmd, is used to make order entries. Its IdevCmd interface exposes a single method, Order, which accepts the client and article identifiers as well as the number of articles ordered. Order adds a new order entry in the database.


(...)
SUCCEEDED( hr = ccpIRSOrders->AddNew( ccvaBlank, ccvaBlank ) ) &&
SUCCEEDED( hr = ccpIRSOrders->get_Fields( &ccpIFields ) ) &&
SUCCEEDED( hr = ccpIFields->get_Item( CComVariant(L"ClientID"), &ccpIField[0] ) ) &&
SUCCEEDED( hr = ccpIField[0]->put_Value( CComVariant(lClient) ) ) &&
SUCCEEDED( hr = ccpIFields->get_Item( CComVariant(L"ArticleID"), &ccpIField[1] ) ) &&
SUCCEEDED( hr = ccpIField[1]->put_Value( CComVariant(lArticle) ) ) &&
SUCCEEDED( hr = ccpIFields->get_Item( CComVariant(L"Quantity"), &ccpIField[2] ) ) &&
SUCCEEDED( hr = ccpIField[2]->put_Value( CComVariant(lQuantity) ) ) &&
SUCCEEDED( hr = ccpIRSOrders->Update( ccvaBlank, ccvaBlank ) ) &&
(...)

This component also implements the IObjectConstruct interface, but only stores the DSN value without creating a database connection. Why? We'll see later. You may also wonder why we need two components instead of one. The answer lies next.

COM+ Queued Components

A queued component is a component that gets its methods and properties called after a client effectively completes its work on it. The client calls are recorded by a proxy and "played back" on the real component later. For this reason, all queued interfaces must not expose [out] parameters. This is why we created two components: we wanted only one IDispatch-derived interface by component and could not have the get properties and the Order method in the same interface.

To make the IdevCmd interface support queuing, we include mtxattr.h in the .idl file and mark the interface as QUEUABLE (this will automatically set the Queued attribute in the interface properties of the COM+ Application):


[
  object,
  uuid(9F7C40C7-7ABB-442F-BAD5-1A0F82AF3006),
  dual,
  helpstring("IdevCmd Interface"),
  pointer_default(unique),
  QUEUEABLE
]
interface IdevCmd : IDispatch
{
  [id(1), helpstring("method Order")] 
  HRESULT Order( [in] long lClient, [in] long lArticle, 
                 [in, defaultvalue(1)] long lQuantity);
};

COM+ Events

There is a subproject called ShippingEvent. This project contains what is called an Event Class (ShippingEvent.CmdEvent). This is an empty component that exposes an interface used for event notification. The implementation of the OnOrder method does nothing. This component will be used to register the event.


interface IdevCmdEvent : IDispatch
{
  [id(1), helpstring("method OnOrder")] HRESULT OnOrder();
};

Back in the Shipping project and the Shipping.Cmd component, the CmdEvent is used in the Order method to raise the OnOrder event.


if ( SUCCEEDED( ccpIEvent.CoCreateInstance( CLSID_CmdEvent ) ) )
  ccpIEvent->OnOrder();

The last component of the Shipping project is Shipping.Delivery. As its name implies, this is the component in charge of handling orders and shipping articles to the clients. The particularity of Shipping.Delivery is that it exposes the IdevCmdEvent interface. This will enable the component to subscribe for automatic notification of orders. Shipping.Delivery naturally does not implement the articles shipment, as this would require a proper framework.

Note that Shipping.Cmd and Shipping.Delivery are implemented without event load optimization. It would be better to have a mechanism firing fewer events than one that fires one event by order entry.

COM+ Transaction support

When Shipping.Delivery receives an order notification, many things must be done: update the client credit, ship the requested number of articles and mark the order as being dealt with. If any of these operations fails, any changes to the database must be rolled back. COM+ transaction support helps doing just that. To mark Shipping.Delivery as requiring a transaction, we use the TRANSACTION_REQUIRED attribute in the .idl file:


[
  uuid(BA4F38E0-1AA3-4964-BE61-2AA5E0C56D60),
  helpstring("Delivery Class"),
  TRANSACTION_REQUIRED
]
coclass Delivery
{
  [default] interface IdevCmdEvent;
};

Then, in the OnOrder method, we specify our transaction vote (the Happy bit) in regard of the transaction outcome. If the transaction aborts, SQL will automatically rollback any temporary changes made to the database.


if ( SUCCEEDED( hr ) )
{
  // Accept our part of the transaction.
  ccpIState->SetMyTransactionVote( TxCommit );
  return S_OK;
}

You must create the ADODB.Connection component in the context of the transaction. This is why the connection is not kept as a class member in Shipping.Delivery and Shipping.Cmd, which also supports the use of transactions.

Registering the Components

Before running the application, you must make the proper component registrations. You must open the Component Services, which can be found in the Administrative Tools of the Start menu. Under MyComputer\COM+Application, create a new, empty, COM+ Application named Shipping by right-clicking "COM+ Application".

Then, register ShippingEvent.dll by adding a new Component and selecting "Install new event class". Once done, you can go to the Advanced tab of ShippingEvent.CmdEvent's properties and give it a Publisher ID of "Order Event".

Now, register the Shipping.dll components by selecting "Install new components". For each of the Shipping components, you must activate object construction in the Activation tab and enter the constructor string (the SQL connection string):


DRIVER=SQL Server;SERVER=(local);DATABASE=Shipping;Trusted_Connection=Yescode

In the same tab, activate the object pooling and specify some minimum value for the number of components in the pool. For the Shipping.Delivery component, indicate a minimum and maximum value of 1. This will automatically enable the order processing mechanism.

Shipping.Delivery must also subscribe to the OnOrder event. Do so by right-clicking "Subscription" under the component's name and add a new one. Select the OnOrder method followed by the ShippingEvent.CmdEvent and enter "Order Handler" as a name for the new subscription. Finally, check the "Enable this subscription immediately" box before completing the registration.

The last thing to do is to enable Queuing for this COM+ Application: check both boxes of the Queuing tab in the application properties.

Running the Application

You can run the application by executing the .vbs files contained in the Script folder. There you can select a given client and make him buy articles of the simulated market. As you make order entries, you'll see the client credit change. Look at the script code and familiarize yourself with queues. Alter components attributes in the COM+ Application and components behavior in the source code, and see the impacts on the sample application.

Final Hint

This can save you some time: to debug a registered component, you must use dllhost.exe as the executable for the debug session and pass /ProcessID:{COM+ Application ID} as parameter. The Application ID can be found in the General tab of the properties of the COM+ Application.

Summary

We've seen how COM+ Services can be used effectively with a very small amount of code and time. They help us build shared resources pools, asynchronous requests, asynchronous event notifications, and offer powerful transactions support.


About the author

Martin Lapierre has a B.Sc. in computer science. Introduced to COM two years ago, he has breathed and lived a COM life ever since. Martin currently works in Montreal, Canada. You can reach him at the following address: [email protected].

What do you think of this article?

Have your say about the article. You can make your point about the article by mailing [email protected] (If you haven't allready joined, you can join by going to onelist.com/community/dev-com).

You can also write a review. We will publish the best ones here on this article. Send your review to [email protected].

Mail a question to the author!!

As part of the IDevResource commitment to Open Publishing, all of our authors are available to answer all of your trickiest questions at Author Central. For information about the authors, or to mail a question, visit them at Author Central.


Power your site with idr newswire

Contribute to IDR:

To contribute an article to IDR, a click here.

To contact us at IDevResource.com, use our feedback form, or email us.

To comment on the site contact our webmaster.

Promoted by CyberSavvy UK - website promotion experts

All content © Copyright 2000 IDevResource.com, Disclaimer notice

Java COM integration

WTL Introduction

Join the Developers Webring

Learn C#

Visit our NEW WTL Section