This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.

MIND

Automating COM+ Administration
Ted Pattison
Code for this article:Basic0900.exe (70KB)
D

evelopers often find that they have to assume the role of user when designing or testing an application. Frequently, developers must also wear an administrator's hat. How many times this year have you installed and configured a software product such as SQL Server™ or Microsoft® Message Queue Server (MSMQ)?
      When you're developing and testing configured components for COM+, you simply must be a part-time administrator. COM+ applications have to be created. Your components have to be installed and properly configured. And while administering a COM+ application is far less exciting than writing the actual code for your components, it's essential. Someone must take on the task, and that someone is often you. I'll show you how to automate COM+ administration to lighten the burden of these tasks.

What's Your Motivation?

      Many COM+ developers aren't motivated to learn to write the code to automate COM+ administration. After all, the most common administrative chores, such as creating a COM+ application and installing a component, can be accomplished manually with the Component Services administrative tool. Moreover, once you've created and properly configured a COM+ application, you can then use the Export command in order to create a server-side setup program to automate installing your application on multiple production servers.
      However, there are still quite a few occasions when writing the custom code to create and configure a COM+ application is valuable. For example, there are several application and component attribute settings that are not accessible through the Component Services administrative tool; they're only accessible programmatically. As you'll see later, writing code to modify these otherwise inaccessible settings lets you do things such as lock down your COM+ applications and optimize your configured components.
      Programmatic access to attribute settings can also be valuable at times when it's necessary to reconfigure applications and components that have already been put into production. For example, imagine you have a configured component with a custom constructor string that's been deployed on 10 different servers in a Web farm. If you were required to change this constructor string on a regular basis, it would be very tedious to do so by hand using the Component Services administrative tool. It would be far more efficient to automate the process by writing an administrative application or script that could complete the task for you.

Using the COMAdmin Library

      The programmatic administration of COM+ applications and configured components is made possible through a set of system-supplied components known as the Component Services Administration (COMAdmin) Library. The COMAdmin components are part of the default installation of Windows® 2000 and are distributed in COMADMIN.DLL. You should note that these components expose dual interfaces, which means they can be accessed by scripts through late binding or by Visual Basic®-based applications through direct vTable binding.
      In order to program against the COMAdmin components from a Visual Basic-based application you should select the COM+ 1.0 Admin Type Library from the Visual Basic Project | References dialog. In this month's column, I'm going to present examples written in Visual Basic. You can convert these examples to VBScript for use in a Windows Script Host (WSH) file by removing the specific data types from all the variable and parameter declarations. (All variables and parameters in VBScript are must be variants and must be declared without a type.)
      If you take a quick look at the COMAdmin type library with the Visual Basic object browser, you'll see that it's not very revealing. It doesn't tell you much about the COM+ Admin object hierarchy because it only contains three components. The COMAdminCatalog component represents the COM+ Catalog Manager. The two other components, COMAdminCatalogCollection and COMAdminCatalogObject, represent every other object in the hierarchy.
      The COMAdmin object model contains quite a few logical collections such as Applications, Components, and Roles. However, the object model is a bit tricky when you first start programming because all of these collections map to the COMAdminCatalogCollection component. Similarly, there are several logical object types such as Application, Component, Role, and User that all map to the COMAdminCatalogObject component.
Figure 1 ComAdmin Collections
Figure 1ComAdmin Collections

      It's important to know that all the collections in the COMAdmin object model are structured into a single hierarchy. Figure 1 shows some of the more common collections you might encounter. The COMAdminCatalog serves as a root object that contains the Applications collection. The Applications collection is a set of Application objects that represent the COM+ applications on the local computer. The Components collection and the Roles collection are children of the Applications collection. The RolesFor-Component collection is a child of the Components collection. The Users collection is a child collection of the Roles collection.
      What's not so intuitive about the COMAdmin object model is that you cannot access a child collection from its parent object. For example, an Application object does not expose its own Components collection or Roles collection. Instead, when you want to retrieve the Components collection or the Roles collection for a specific application, you must invoke a method on the Applications collection object. However, once you understand this basic premise, the object hierarchy is fairly simple to navigate.

The Catalog Manager

      Every computer running Windows 2000 has a special COM+ application called the System Application. It contains the COM+ Catalog Manager component (Catsrv.Catalog-Server.1). The Catalog Manager is critical to COM+ administration because it abstracts the tasks of reading and writing to the COM+ registration database (RegDB), as shown in Figure 2.
Figure 2 Catalog Manager Architecture
Figure 2Catalog Manager Architecture

      For example, when you use the Component Services administrative tool to create and configure COM+ applications, it uses the Catalog Manager behind the scenes to do its work. Likewise, when an application or script uses the COM+ Admin components, they also rely upon the COM+ Catalog Manager to perform administrative tasks.
      You must begin any administrative session by establishing a connection to a Catalog Manager on a specific computer. You can establish a connection with the Catalog Manager on a local computer with the following code:

Dim cat As COMAdminCatalog
Set cat = New COMAdminCatalog

If you'd like to perform an administrative task on another computer, you can establish a remote connection to the Catalog Manager like this:

Dim cat As COMAdminCatalog
Set cat = New COMAdminCatalog
' connect using a NETBIOS name, an IP address or a DNS name
cat.Connect "MyProductionServer"

      Note in either the local or remote scenario, an application or script will not be able to perform administrative tasks unless it's running under the identity of a user who has the proper authority. By default, the user must be in the local Administrators group of the computer on which the administrative work is being performed. However, it's possible to grant administrative privileges to other users and groups by adding them to the Administrators role in the System Application if they're in an Administrators group.
      You should become familiar with the other methods exposed by the COMAdminCatalog component. This component makes it possible to export and import COM+ applications as well as back up and restore RegDB. The COMAdminCatalog component also exposes methods that make it possible to start and to shut down a server application.

Creating a New COM+ Application

      Let's jump right in now and look at the code required to create a new COM+ application. Then I'll show you the code required to install the components from a DLL.
      Take a look at the code in Figure 3. This code sample creates an application by adding a new COMAdminCatalogObject object to the Applications collection. However, in order to add a new application you must first retrieve the Applications collection by invoking the GetCollection method on the Catalog Manager object. Once you've created a new Application object, you can assign it an AppID and a name. (You can generate an AppID for your application using the GUIDGEN.EXE utility that ships with Visual Studio®.) Note that just like other COMAdmin objects, you assign attribute settings for an Application object by writing to the Value property, which exposes a dynamic collection of its properties.
      The configuration data for the new application is not actually written back to RegDB until the SaveChanges method has been invoked on the Applications collection. This convention of caching a set of collectionwide changes and then saving them all at once is consistent throughout the COMAdmin programming model. You should also note that the Catalog Manager always uses a COM+ transaction when you call SaveChanges to write all your modifications back to RegDB as an all-or-nothing proposition.
      There are a few important things you should know about creating a new COM+ application programmatically. First, while the COMAdminCatalogObject exposes a Name property that holds the application's name and a Key property that holds the application's AppID, you cannot assign values to these properties when you've just created a new application. Instead, you must use the Value property to assign these attributes, as shown in Figure 3. However, once a COM+ application has a name and an AppID, you can query the Name and Key properties to obtain their values.
      The second thing to note is that the Catalog Manager assigns the application an AppID that's automatically generated when you invoke the SaveChanges method, unless you explicitly assign one yourself. I have found that if you explicitly assign custom AppIDs to your COM+ applications, it makes it easier to write administrative code for them. This is especially true if you're going to install and maintain the same application on multiple production servers as is often done in a Web farm environment. As you'll see a little later, it's easier to install components and reconfigure component attributes when you know the AppID of the hosting application ahead of time.
      The third thing to note about creating an application and explicitly assigning it an AppID is that it will overwrite any existing application that has the same AppID when you call SaveChanges. My intuition told me that attempting to create a second application with a duplicate AppID should fail. However, this is not the case. At times this behavior is handy because rerunning a routine such as the one shown in Figure 3 will simply overwrite your previous settings. However, you have to be careful. You can lose existing application attribute settings, including those that have been configured by hand using the Component Services administrative tool.
      The fourth thing you have to watch out for is the possibility of creating two or more applications with the same name. For example, if you remove the line of code that assigns the custom AppID from Figure 3 and run the code multiple times, it will result in the generation of multiple applications with different AppIDs, but the same name. This is obviously something that should be avoided. While the Component Services administrative tool prevents you from creating two applications with the same name, there's nothing (other than writing sensible code) that prevents you from doing it when you're programming against the COM+ Admin components.
      In addition to assigning the new application a name and an AppID, the sample code in Figure 3 also assigns a few other application attributes. This sample code assigns a Description attribute to the application and configures it to run as a library application (as opposed to a server app) by adjusting its Activation setting.
      I'm not going to go through all the possible Application attributes and their available settings because this information is available online. The COM+ Administration Reference in the Platform SDK documentation briefly explains each configurable attribute for objects such as applications and components. The topic named "Applications Collection" lists each application attribute, while "Components Collection" lists each component attribute. If you plan to program using the COM+ Admin components, you should take the time to become familiar with these resources.

Installing Components

      The COM+ programming model requires each configured component (as identified by its CLSID) to live within the scope of a COM+ application. There is a also a restriction that limits any CLSID from being associated with more than one COM+ application per computer.
      Once you've created a COM+ application, it's pretty easy to install the components from a DLL.

' Install component(s) in DLL into the new application
Const AppID = "{DEADBEEF-BADD-BADD-BADD-2BE2DEF4BEDD}"
Const ServerPath = "c:\MyApplication\MyServer.dll"
Dim cat As COMAdminCatalog
Set cat = New COMAdminCatalog
cat.InstallComponent AppID, ServerPath, "", ""

You simply invoke the InstallComponent method of the Catalog Manager and pass the AppID or the name of your application and the path to your component's DLL. The InstallComponent method accepts the path to an external type library and a custom proxy/stub DLL as the third and fourth parameter. If you don't care about either of these, you must still pass two empty strings because these parameters do not have default values.
      The Catalog Manager exposes a second method for component installation named InstallMultipleComponents. This method allows you to selectively install specific components from one or more DLLs. However, this method is harder to call because you must pass a string array of DLL file names as well as a string array of CLSIDs.
      In most cases, it's best to package components together into the same DLL only when they will be installed into the same COM+ application. Although COM+ makes it possible to add two components from the same DLL into different COM+ applications, this approach is just going to create complications and increase the chances of things going wrong during deployment. Moreover, when all of the components from any one DLL are targeted for the same COM+ application, you can always use InstallComponent instead of having to resort to calling InstallMultipleComponents.

Working with COMAdmin Collections

      In many circumstances, you'll find it necessary to populate a collection before using it. For example, let's say you wanted to write a function to determine whether a COM+ application with a specific name already exists. As you can see in Figure 4, invoking the GetCollection method by itself does not retrieve the actual Application objects from RegDB. You need to invoke the Applications Collection's Populate method to look through the set of existing COM+ applications. As you can see in Figure 4, the COMAdminCatalogCollection components supports COM enumeration, which makes it possible to move through any COMAdmin collection using a simple for�each loop.
      Things would be a little easier if the COMAdminCatalogCollection component provided some type of searching functionality. For example, when you're using a Visual Basic for Applications Collection object and you know the key of the item you'd like to retrieve, you can pass the key value to the collection's Item property in order to fetch the item. The COMAdminCatalogCollection component doesn't provide any similar functionality. When you want to locate an object within a collection, you have to loop through the entire collection and search for the object using either the object's name or key value.
      You might notice that the COMAdminCatalogCollection component contains two methods, PopulateByKey and PopulateByQuery. It seems reasonable to conclude that methods with these names would assist you in searching through a collection and allow you to avoid an object-by-object search. Unfortunately, you cannot call the PopulateByKey method because its parameter list is incompatible with Visual Basic. The PopulateByQuery method is reserved for future use and is not currently implemented.

Modifying the Attributes of an Existing App

      Let's look at an example administrative chore where it's necessary to enumerate through the Applications collection to locate a specific Application object. Each Application object has a Changeable attribute and a Deleteable attribute that can be configured to prevent careless or malicious modifications to your application's configuration settings. Note that these two application attributes cannot be modified from the Component Services administrative tool. You should consider this a good thing. After all, the main motivation for adjusting the Changeable and Deleteable attributes is to lock down a COM+ application so an overly curious user or administrator can't wreak havoc on your application by fooling around with the Component Services administrative tool.
      As you can see from the custom utility procedure in Figure 5, you can simply loop through the Applications collection and locate a specific Application object either by name or by its AppID. Once you locate the Application object you're looking for, simply modify the desired properties and then save your changes by invoking the SaveChanges method on the Applications collection.
      You can call the EnableApplicationChanges procedure, passing the application's name and a value of False for the second parameter. This will prevent others from modifying your configuration settings in a COM+ application. You can call this procedure a second time, passing a value of True for the second parameter if you need to delete the app or modify its configuration settings.

Reconfiguring Component Attributes

      As I mentioned earlier, one of the biggest motivations for learning how to program with the COM+ Admin components is to adjust the attribute settings of your configured components. This is especially true of attribute settings that are not accessible through the Component Services administrative tool. Let's look at an example of reconfiguring a few attributes of a component that has already been installed in a COM+ application. As you can see from the code in Figure 6, it's easier if you know the AppID of the hosting application and the CLSID of the component beforehand.
      This is the first example where I have shown how to access a collection within a collection. Note that you must retrieve the Components collection for a specific application by calling the GetCollection method of the Applications collection. When you call GetCollection you must pass the Key property (in this case the AppID) of the object whose child collection you'd like to examine. If you don't know the application's AppID ahead of time, you must loop through the Applications collection, locate the appropriate Application object, and then query it for its Key value.
      As in the case of searching for a particular Application object, you must populate the Components collection and loop through it to locate the desired Component object by searching for its Key or Name property. Note that a Component object's Key value is its CLSID, and its Name property is its ProgID.
      Once you've located the appropriate Component object, you can reconfig-ure any of its attributes by writing to its Value property, as shown in Figure 6. This example shows how to reconfigure component attributes such as the constructor string and the just-in-time activation setting. The example also demonstrates how to perform a minor component optimization by disabling the IISIntrinsics attribute and the COMTIIntrinsics attribute. These two component attributes are disabled by default and they aren't usually necessary. Furthermore, neither of these attributes are accessible through the Component Services administrative tool, so you have to disable them programmatically.

Adding Roles and Configuring Security

      Figure 7 shows how to add roles to an application and associate them with a specific component. As you can see, the code in Figure 7 is very similar to other examples I've shown earlier. It's all about navigating through the object hierarchy and populating collections to add and find the objects you need to reconfigure. Once you've added a role to the Roles collection of an application, you can add users and groups to the role through the role's UsersInRole collection. Once you've added and configured the role, you can associate with your component by adding a new object to the component's RolesForComponent collection.
      The only thing that makes writing this code somewhat tricky is that you have to navigate down through three levels in the COMAdmin collection hierarchy. Imagine what you would have to do if you wanted to configure role-based security at the method level instead of the component level. You would have to navigate down through both the InterfacesForComponent and the MethodsForInterface collection to add a new object to the method's RolesForMethod collection. I leave this as an optional exercise.
      Note that when you configure role-based security, there are a few other important application and component-level attributes to set. You have to make sure the application's ApplicationAccessChecksEnabled attribute is enabled and that the AccessChecksLevel attribute is configured for component-level checks. You also have to make sure that the component's ComponentAccessChecksEnabled attribute is enabled.

Conclusion

      There are times when you need to write code to automate administrative tasks in both the design and production environments. In the design environment, the motivation is often to reconfigure application and component attributes that are inaccessible through the Component Services administrative tool. In a production environment, the motivation is to create an application or scripts that make it more efficient to deploy a COM+ application and conduct ongoing maintenance.
      This month you've seen the basics of writing custom code to automate COM+ administration through the use of the COM-Admin component library. If you'd like to play with the code samples from this month's column, you can download them from the link at the top of this article. The download includes a QuickViewer sample app that lets you inspect all the application and component-level attributes.
      While the hierarchy of the COMAdmin component library might not be as straightforward as others, it's fairly simple to use once you learn how to navigate through its collection. Furthermore, the more often your code can play the role of the administrator, the less often you'll have to.

Ted Pattison is an instructor and researcher at DevelopMentor (https://www.develop.com), where he co-manages the Visual Basic curriculum. The second edition of Ted's book, Programming Distributed Applications with COM and Visual Basic 6.0, was recently published by Microsoft Press.

From the September 2000 issue of MSDN Magazine.