icrosoft® Windows® XP brings with it the next version of COM+, which at press time is called COM+ 1.5. This article describes the new features and capabilities in this upcoming release. Microsoft has improved COM+ usability in a number of ways, and they've addressed some of the existing pitfalls of COM+ 1.0. Microsoft also added new features to existing services and laid the foundation for integration with Microsoft .NET services. Rest assured that COM+ 1.5 is fully backward compatible with COM+ 1.0 components and applications.
The information in this article is based on a prerelease copy of Windows XP and COM+. Some details may change in the actual released product.
Legacy Applications and Components
The first thing you notice about the COM+ 1.5 Explorer is the improved user interface. For example, under COM+ 1.0, the only way to tell the activation type of a COM+ application was to bring up its Activation tab and examine it. The COM+ 1.5 Explorer assigns different icons to different application types (see Figure 1), so just by viewing the application you can deduce its type (server, library, or proxy).
Figure 1 COM+ 1.5 Explorer
Service applications, a fourth application type that is available in COM+ 1.5, also have a distinct icon. A new folder which can be found under My Computer, called Running Processes, contains all the currently executing applications, providing easy runtime administration.
The COM+ 1.0 Explorer only allows you to manage configured components. If your product is made up entirely of configured components, then that limitation is probably fine for you. However, not all developers are that lucky. In real life, configured components often have to interact with in-house or third-party legacy COM components. In such a heterogeneous environment, developers use other tools in addition to the COM+ Explorer to manage legacy components. These tools include DCOMCNFG, OLEView, Visual Studio®, or custom tools.
Developers also have to manage two types of deployment approaches—one uses exported COM+ applications (MSI files) while the other consists of whatever they need to do to install their particular legacy components. The good news is that complete support for legacy applications and components is new in COM+ 1.5. This allows you to manage every aspect of your legacy applications and components just as well as you could using DCOMCNFG or OLEView.
In the COM+ 1.5 Explorer, under My Computer there is a new folder called DCOM Config. This folder is a sibling to the COM+ Applications folder (see Figure 1). The DCOM Config folder contains all the registered COM local servers (EXE servers) on your machine. Each local server is called a legacy application. Unlike a COM+ application, you cannot expand a legacy application down to the component, interface, or method level. A legacy application is opaque as far as COM+ 1.5 is concerned. The DCOM Config folder simply gives you a centralized place to manage both your COM+ applications and your legacy local servers, without resorting to other utilities.
Figure 2 Legacy App Properties Page
When you right-click on a legacy application and select Properties from the popup context menu, you get a properties page that lets you manage every aspect of the legacy application, very much like what DCOMCNFG provides (see Figure 2). The General tab lets you change the application name and set the authentication level for incoming calls to this application. The Location tab lets you control whether to run the application on your computer or on another computer on the network. The Endpoints tab lets you configure the transport protocols for the DCOM calls. The Identity tab lets you specify under which security identity to launch the server, including the system account (for services only). Finally, the Security tab lets you configure access, launch, and change permissions on a per-user basis.
If your COM+ application makes use of legacy components (nonconfigured in-proc COM components), then the COM+ 1.5 Explorer lets you manage those within the scope of your application as well. Every COM+ 1.5 application has a new folder called Legacy Components (see Figure 1). To add a legacy component to the folder, expand it, right-click on it, and select New from the context menu. The COM+ 1.5 Explorer brings up the Legacy Component Import Wizard. The wizard lets you choose legacy components to add to your application. You should note that just like configured components, legacy components can take part in exactly one COM+ 1.5 application.
The major benefit of having your legacy components as part of your COM+ 1.5 application is easier deployment. When you export a COM+ application, its MSI file contains the legacy components and their settings. When you install the MSI file on another machine, the Windows Installer registers the components, which saves you the trouble of writing a separate installation program for each project.
Figure 3 Legacy Component Properties Page
The Properties page of a legacy component presents you with every relevant registry entry for that component (see Figure 3). You can only change the values of settings that do not collide with registry settings in the component itself. For example, you cannot change the threading model value, but you can provide the name of a surrogate process. You can even promote a legacy component to a configured component. Simply bring up the legacy component's context menu, then select Promote (see Figure 4). The legacy component is removed from the Legacy Components folder and added to the Components folder in the same COM+ 1.5 application.
Figure 4 Promoting a Legacy Component
Disabling Applications and Components
The COM+ 1.5 Explorer lets you disable applications and components. When you disable an application, all client attempts to create any component from that application fail, with the following message associated with the HRESULT: "The component has been disabled." To disable an application, display its popup context menu and select Disable. When an application is disabled, a red square (like a music player's Stop button) is displayed on its icon in the COM+ 1.5 Explorer. To enable a disabled application, bring up the context menu again and select Enable (see Figure 5).
Figure 5 Enabling a Disabled Application
You can only disable a COM+ 1.5 application; legacy applications cannot be disabled. Interestingly enough, a client that already has a reference to a COM+ object is not affected by the fact that the application has been disabled. Only clients that try to create new objects are affected. Consequently, you can have a disabled application running indefinitely.
You can also disable components individually. Every component popup context menu has a Disabled option. A disabled component also displays a red square on its icon. All client attempts to create a disabled component fail, returning a message stating that the component has been disabled.
You can disable any component in a COM+ 1.5 application, including legacy components (as you saw in Figure 4). To enable a component, select Enable from its context menu. Disabling a component only affects new activation requests; existing references to objects are not affected. Enabled status for both applications and components is stored in the COM+ Catalog, and is therefore maintained even when you reboot the machine.
Disabling applications or components is useful in two cases. The first is when you want to gracefully shut down an application on a live server machine in order to perform maintenance or upgrades. If you were simply to shut down the application, then you might cause unexpected failures on client machines holding existing references. By disabling an application, you can have existing clients finishing their work, while new activations are perhaps routed to another machine, providing you the opportunity to perform maintenance. Disabling an application is also useful during development and testing. It provides a guaranteed way for failing client calls and thus is a way to test your client-side error handling.
Pausing an application is similar to disabling an application, except it is used to disable a particular running process only, and the paused status does not survive an application shutdown. To pause a process, open the Running Processes folder and select Pause from the application's context menu. When you want to resume it, choose Resume (see Figure 6).
Figure 6 Resuming an Application
Service Activation Type
COM+ 1.5 lets you configure a server application to run as a system service. Doing this allows you to have your application running as soon as the machine boots, independent of client activation requests. Another benefit is that a service application is the only way to have the application run under the system identity account. The system account is the most powerful account on a given machine.
The application Activation tab contains the checkbox "Run application as NT Service" (see Figure 7). When selected, you can also configure the various service parameters by clicking the Setup New Service button, saving you the trouble of using the control panel services applet.
Figure 7 Configuring a Service Application
Improved Queuing Support
Queued components under COM+ 1.0 require the presence of a domain controller to provide authentication for the queued call. If you do not have a domain controller, you must turn off COM+ 1.0 application authentication (that is, set it to None). COM+ 1.5 provides better configurable security support for queued calls by separating them from normal synchronous calls. The application Queuing tab now lets you configure authentication for queued calls explicitly (see Figure 8). You have the following options to choose from:
Figure 8 Application Queuing
- Use Microsoft Message Queuing (MSMQ) domain controller authentication when the application is configured to use authentication for synchronous calls (when the application authentication is set to any value except None).
- Never authenticate queued calls into this application. Choosing this option allows you to freely use queued components without a domain controller.
- Always authenticate incoming queued calls, regardless of the application authentication setting.
The Queuing tab also allows you to control the maximum number of concurrent players the application can contain. (A player is a component that plays back the queued calls to your component.) Since every player is created on a separate thread, there is nontrivial overhead associated with creating and maintaining a player. In extreme situations, your application may grind to a halt if the number of concurrent players is too large (such as a few hundred). When you set a limit to the number of players and that limit is reached, the listener does not create new players. Rather, queued calls remain in the application queue, allowing calls in progress to execute and complete. The limit is also good for load balancing purposes, and can be used in conjunction with application pooling.
Application Pooling and Recycling
COM+ 1.5 provides two new application lifecycle management options, called application pooling and recycling. Both are configurable from the new Pooling & Recycling tab on the application's properties page (see Figure 9). Pooling and recycling services are available only for a server application. You cannot configure library applications to use pooling and recycling since they do not own their hosting process. Library applications have, in effect, the pooling and recycling parameters of whichever server application happens to load them.
Figure 9 Application Pooling & Recycling
Application pooling allows you to configure the number of surrogate processes launched to host your application's components. Under COM+ 1.0, all instances of components from a server application always share the same hosting process. Although this is also the classic COM default, classic COM local server developers also had the option of allocating one process for each object (by registering the class factories with the REGCLS_SINGLEUSE flag). COM+ 1.5 gives you explicit control over how many processes are launched by configuring a process pool. You configure the pool size in the Pool size edit box (see Figure 9). The default pool size is 1—a single process hosts all instances of components from the application, just like in COM+ 1.0. However, if you set it to a value greater than 1, COM+ 1.5 creates a process for each new instance until it reaches the pool size, at which point COM+ starts multiplexing new instances to the existing processes, apparently in a round-robin fashion. The maximum configurable pool size is 999,999, enough for all practical purposes.
Application pooling is useful as a fault isolation technique. If one process has to shut down because of some error or exception, the other processes and their clients are not affected. Application pooling also gives you same-machine load balancing—you do not have to use a separate load balancing service and multiple machines to allocate different instances to different processes with COM+ 1.5.
The other new application lifetime management service is recycling. Application recycling is used to increase overall application robustness and availability by compensating for code defects. For example, one of the most common defects is a memory leak. Not all products have the necessary quality assurance resources or commitment during development and, as a result, memory leaks can occur even in released products. Even a very small memory leak can have a devastating effect over a long period of time.
For example, imagine a system with an "insignificant" memory leak of only 10 bytes per method call. A modern system that processes in excess of 100 method calls per second will, after one day, leak 100MB of memory. The process hosting the application will consume this amount of memory, which will severely hamper performance and availability, due to the additional page faults and memory allocation penalties.
The way developers treated such a leak in COM+ 1.0 (besides fixing it) was to periodically terminate the hosting process and restart it. This technique is called application recycling. COM+ 1.5 allows you to configure automatic recycling on the Pooling & Recycling tab. To cope with memory leaks, you can have COM+ terminate the process when it reaches a memory limit (see the Memory Limit edit box in Figure 9). A value of zero (no limit) is the default value.
To cope with defects in handling other kinds of resources (such as system handles), you can instruct COM+ to shut down your application after a predetermined amount of time by specifying the Lifetime Limit value. A value of zero (no lifetime limit) is the default value.
Note that the lifetime limit semantics are different from the idle time management option on the application Advanced tab. The Server Process Shutdown value on the Advanced tab specifies the length of idle time (not servicing clients) after which the application should be shut down. The lifetime value specifies the number of minutes before application shutdown, irrespective of the work in progress inside the process.
COM+ provides two more recycling triggers. You can have COM+ recycle your application after a specified number of method calls into your application by specifying such a limit in the Call Limit edit box. The number of calls is defined as the combined number of calls made on all the objects in the application. The default value is set to zero (no limit). You can also request application recycling after a certain number of activations. Activations are defined as the total number of objects that COM+ 1.5 created in that application. You specify activation limit in the Activation Limit edit box, and again, the default value is set to zero (no limit).
Regardless of how the decision to recycle the application is made (memory limit reached, the lifetime elapsed, call or activation limit was reached), COM+ 1.5 routes a new activation request to a new host process, and waits for existing clients to release their references to objects in the recycled process. However, in the Expiration Timeout edit box you can specify how long COM+ 1.5 should wait. After that expiration timeout, COM+ 1.5 terminates the application, even if there are still clients holding live references. The default expiration timeout is 15 minutes. Finally, you should note that recycling is not available for a COM+ application configured as system service, nor can you recycle a paused application.
The COM+ 1.5 Catalog allows you to programmatically configure the recycling parameters discussed previously. To configure memory and time-bound recycling, use the RecycleMemoryLimit and RecycleLifetimeLimit named properties of the application's Catalog object. To configure the expiration timeout, use the RecycleExpirationTimeout named property. When you want to configure the call or activation limits programmatically, set the RecycleCallLimit or RecycleActivationLimit properties.
Figure 10 shows how to programmatically set a recycling limit. The code implements the SetRecycleByActivations helper function that sets a limit of activations for recycling a specified app.
For debug and analysis purposes, it is sometimes useful to get a complete memory dump of an application, especially at the time of a crash. COM+ 1.5 allows you to configure a dump of a static memory image of the process hosting your COM+ application. You can use a utility such as WinDbg to view and analyze this image.
Every COM+ 1.5 application (including library applications) has a new tab on its properties page called Dump (see Figure 11). You can specify a location for the image dump and the number of images to store there. When the maximum number of images is reached, a new dump image overwrites the oldest one. The maximum number of images you can have COM+ 1.5 store for you is 200.
Figure 11 Application Dump
You can generate a dump file in a number of ways. The first (and most useful) way is to instruct COM+ to dump a memory image on application fault (as shown in Figure 11). An application's fault in this context is when an exception is thrown. The second way is to select Dump from a running application's context menu (see Figure 6). Finally, you can also explicitly request a dump using the DumpProcess method of the ICOMAdminCatalog2 interface, defined as:
[id(0x1f)] HRESULT DumpProcess([in] BSTR bstrApplInstanceId,
[in] BSTR bstrDirectory,
[in] long lMaxImages,
[out,retval] BSTR* pbstrDumpFile);
ICOMAdminCatalog2 derives from the COM+ 1.0 ICOMAdminCatalog, and is supported by the COM+ 1.5 Catalog root object (COMAdminCatalog).
When you use the DumpProcess method, you have to provide the dump directory and file name, and you cannot rely on the configured values. Requesting a dump (either by calling DumpProcess or selecting Dump from the context menu) on a running application is nonintrusive—the process can continue to run, and is only frozen temporarily for the duration of the dump.
When COM+ generates a dumped file, it uses the following naming convention as a file name so that you can easily associate a dump file with a reported system failure.
For example, here is a typical dump file name:
To avoid calling DumpProcess needlessly, ICOMAdminCatalog2 has a helper method called IsProcessDumpSupported, which is used to find out whether image dump is enabled on the machine:
The application partitioning service refines and improves management of COM+ applications in a large-scale environment. An in-depth discussion of application partitions is beyond the scope of this article and requires an understanding of Windows 2000 Server's Active Directory™ service. Instead, this article provides a simplified overview of the partition concept.
An application partition is a group of COM+ 1.5 applications. Partitions provide an economic way of presenting each user (be it a logged-on user or a call coming in across the network) with its own set of applications and components. Partitions are usually configured in Active Directory.
Under COM+ 1.0, a component can only belong to one COM+ application on a given machine. If you want to install the same component (same CLSID) in multiple applications, you have to do so on multiple machines. COM+ partitions allow you to install the same component in more than one application, provided the applications belong to different partitions. A given machine can contain multiple partitions, and each partition can have its own set of applications and components. You assign users to partitions in Active Directory.
COM+ 1.5 also defines a base partition—a partition that all users share. When a user tries to create a component, COM+ first looks in the partition the user is associated with. If that partition has that component, then COM+ creates it. If it does not, COM+ looks in the base partition, and if it is found there, COM+ creates it. If the base partition does not contain the component, then the creation fails, even if the component is part of another partition.
For example, consider the partition layout in Figure 12. If a user associated with partition A tries to create only the component with CLSID1, then that component is created from partition A, and the configuration settings of App1 in partition A are applied, as well as any component-level configuration. However, if the user tries to create the component with CLSID3, then the component from the base partition is created, and the base partition settings are applied. If the user tries to create CLSID7, then the creation fails.
Figure 12 Application Partitioning
Figure 12 shows that a given CLSID can belong to more than one partition, but a given partition can only have one copy of the component. Different partitions can contain applications with the same name. The different partitions inherit the base partition's applications and components, but they can override them, remove them, add new components, and change their settings.
Application partitions provide an easier way for managing activations and isolating applications between partitions. Each partition can be managed independently of the other, and you can even install different versions of the same component in different partitions, thus tailoring a particular compatibility solution.
Under COM+ 1.5, the context object has a partition property. The context object supports a new interface called IObjectContextInfo2 that derives from IObjectContextInfo, which allows you to get information about the partition and the application that the object is part of. Clients can request the creation of an object in a particular partition using a special moniker.
The ICOMAdminCatalog2 interface provides you with numerous methods for managing partitions, including copying an application from one partition to another, copying and moving a component from one partition to another, getting the base application partition ID, and getting the current partition ID.
Under COM+ 1.0 there is no way to use the same component with more than one set of configurations—a component is associated with just one CLSID, just like in classic COM. COM+ 1.5 allows you to alias an existing configured component with a new CLSID and apply a new set of configurations to the "new" component. This is called aliasing a component. Aliasing is quite often a useful feature—you develop a piece of business logic and assign more than one set of configuration parameters to it by aliasing it as much as you like. The component's client can now decide which configuration setting and business logic implementation to instantiate by creating a particular CLSID.
Figure 13 Aliasing a Component
To alias a component, select Alias from its popup context menu in the COM+ Explorer. This brings up the Alias Component dialog (see Figure 13). The dialog lets you select a destination application for the new component. Because you are assigning a new CLSID to the component, you can even copy it back to its current application. The dialog generates the new CLSID for the copy and a new progID (CopyOf.Old prog-ID, as you can see in Figure 13). You can provide your own values for these if you like. Initially, the new component has the exact same configuration setting as the original one. Once you alias a component, the original and the clone are considered different components from the COM+ point of view, and you can configure them differently, even though at runtime the configurations apply to the same actual component.
Configurable Transaction Isolation Level
COM+ 1.0 is very conservative regarding transaction isolation. It allows only the highest level of isolation: serialized. With serialized transactions, the results obtained from a set of concurrent transactions are identical to the results obtained by running each transaction serially. Such a high degree of isolation comes at the expense of overall system throughput because the resource managers involved have to hold on to both read and write locks for as long as a transaction is in progress, and during this time all other transactions are blocked.
However, there are some situations where you may want to trade system consistency for throughput by lowering the isolation level. Let's look at an example. One of the requirements of a banking system is to retrieve the total amount of money in all customer accounts combined. While it is possible to execute that transaction with the serialized isolation level, if the bank has hundreds of thousands of accounts, it may take quite a while to complete. The transaction might timeout and abort because some accounts are being accessed by other transactions at the same time. But the number of accounts may be a blessing in disguise. On average, statistically speaking, if the transaction is allowed to run at a lower transaction level, it may get the wrong balance on some accounts, but those incorrect balances would tend to cancel each other out. The actual resulting error may be acceptable for the bank's need.
COM+ 1.5 allows you to configure the isolation level for a transactional component. The Transactions tab has a dropdown listbox with five isolation levels (see Figure 14). The available isolation levels are Any, Read Uncommitted, Read Committed, Repeatable Read, and Serialized. The default isolation level is Serialized.
Figure 14 Configuring Transaction Isolation Level
The underlying transaction processing monitor, the distributed transaction coordinator (DTC), supports other transaction isolation levels besides Serialized, but it is COM+ 1.0 that passes in a hardcoded isolation level of Serialized when it creates a new DTC transaction. All COM+ 1.5 does to expose these levels is pass the configured isolation level to the DTC, instead of the original hardcoded serialized level in COM+ 1.0.
Selecting an isolation level other than Serialized is commonly used for read-intensive systems, and it requires a solid understanding of transaction processing theory, the semantics of the transaction itself, the concurrency issues involved, and the consequences. In addition, not all resource managers support all levels of isolation, and they may elect to take part in the transaction at a higher level than the one configured. Every isolation level besides Serialized is susceptible to some sort of inconsistency resulting from other transactions accessing the same information.
The difference between the four isolation levels is in the way they use read and write locks. A lock can be held only when the transaction accesses the data in the resource manager, or it can be held until the transaction is committed or aborted. Holding a lock while the transaction is accessing the data is better for throughput, and holding it until the transaction completes is better for consistency. The two kinds of locks and the two kinds of operations (read/write) give you four isolation levels to choose from. (For more information on isolation levels, check out one of the many textbooks available on transaction processing.)
In a COM+ transaction, the root component does more than just start and end a transaction; the root also determines the isolation level for the transaction. Once determined, the isolation level is fixed for the life of the transaction. A component cannot take part in a transaction if the isolation level it requires is greater than that of the transaction. Consequently, every component in a transaction must have its isolation level set to a level equal to or less than that of the root component. If a component tries to create another component with a greater isolation level, COM+ 1.5 refuses to create the component, and CoCreateInstance returns CO_E_ISOLEVELMISMATCH.
When isolation is set to Any, the component is indifferent to the isolation level of the transaction it is part of. If that component is not the root of a transaction, then it simply assumes the isolation level of the transaction it is part of when it accesses resource managers. If that component is the root of a transaction, then COM+ 1.5 decides on the isolation level for it and uses Serialized.
As a result, any component with an isolation level set to Serialized or Any can be the root of a COM+ 1.5 transaction because by definition all other components have isolation levels equal to or less than they do. Any other isolation level for a root may not necessarily guarantee the successful activation of internal components.
Improved Context Activation Setting
Configuring your component to use Just-in-Time (JIT) activation requires having its own context. COM+ 1.0 lets you configure your component to use JIT activation and configure it to require that the component be activated always in its creator's context by checking the checkbox "Must be activated in caller's context" on the component properties page on the Activation tab. These two settings are mutually exclusive. If you configure a component like that, all activations fail. You face a similar pitfall when configuring the component to use transactions or enforce security access checks for the component—all require a private context.
Figure 15 Component Activation
The COM+ 1.5 Explorer has a remedy for this situation. It includes a redesign of the component Activation tab (see Figure 15) as well as a new activation option. The Activation Context properties group contains three radio buttons. You can only select one button at a time. This enforces mutual exclusion. If you select "Don't force activation context," you actually select the regular COM+ context activation behavior. In this mode, you can enable JIT activation, transactions, and security access checks. In fact, as long as transaction support or access security are enabled, you cannot even select another option, and enabling security checks sets the selection back to "Don't force activation context" from any other setting.
COM+ 1.5 added a new context activation selection—"Must be activated in the default context." This new option can be useful when you know that clients of the component reside in the default context, that they are making frequent calls of short duration to your component, and that your component does not use almost all of COM+ services.
COM+ 1.5 provides a new feature called private components. Every component at the bottom of its Activation tab has the "Mark component private to application" checkbox (see Figure 15). A private component can only be accessed by other components in the same application. Private components are needed in almost every decent-sized COM+ project. To promote loose coupling between clients and objects, you should not allow the clients to access the internal objects by marking them as private.
Web services support is the most exciting new service of the .NET platform. Web services allows a middle-tier component in one Web site to invoke methods on another middle-tier component at another Web site, as easily as if the two components were on the same site and machine. But .NET Web services comes with a price—companies have to invest in learning new languages such as C#, and cope with a new programming model and new class libraries. In most organizations, this is a nontrivial cost. To preserve existing investment in COM components and development expertise while providing a migration path to .NET, COM+ 1.5 can expose any COM+ component as a Web service as long as the component complies with Web services design guidelines. The application Activation tab lets you configure SOAP activation for your application (see Figure 7). All you need to do is specify the virtual directory of the Web service associated with this application, and COM+ provides the necessary adaptors as a component service. COM+ installs the Web service with IIS, and generates the proper Web service configuration and information files. You should note that IIS must be installed on your machine to enable SOAP activation mode for your application.
COM+ is essential for rapid component development and robust, scalable applications. COM+ allows developers to focus on the business problem at hand, instead of implementing services plumbing such as object and application lifecycle management, transaction support, concurrency management, security, asynchronous calls, events, and deployment. COM+ 1.5 smoothes the rough edges that COM+ 1.0 has, and its features are a most welcome addition to your development arsenal. With COM+ 1.5, Microsoft is also reaffirming its commitment to the development paradigm of separating the business logic (what developers are responsible for) from the supporting services (what COM+ provides). Because COM+ is the basis for all of .NET component services, no doubt this is not the last release of COM+, and Microsoft will continue to add new features and capabilities to COM+ to improve and enhance its integration with .NET.