As developers, we are trained to look at workflows, trends and needs, perform an analysis of our own personal experience, and recognize repetitive actions that could benefit from automation. So it is surprising that when you ask a developer “How much time have you spent writing applications that automate your own job?” the answer is usually “None.” All too often we are so busy making our users’ lives easier that we fail to recognize opportunities to enhance our own productivity.
One of the most common enterprise VB development tasks is registering COM objects to a development server. Registering objects, although not difficult, can certainly become monotonous. Many developers spend more of their day than they would care to admit in the Windows and MTS Explorers, dragging files from here to there, right-clicking this and that, just to register a component. Fortunately, Microsoft has provided us with the ability to access most of the MTS Explorer’s functionality via the MTXAdmin type library. This article explores a few of MTXAdmin’s features and demonstrates how to embed some of them into a COM component of your own. You’ll also see how to write a VB add-in that will allow you to register your components without ever leaving the Visual BASIC IDE.
|Figure 1: The MTS Explorer.|
MTXAdmin’s object hierarchy is tied very closely to the structure shown in the MTS Explorer (see Figure 1). Start with a top-level object, MTSAdmin.Catalog, which exposes a collection of packages. Each package object has a collection containing zero or more component objects. Note that the component object also abstracts other objects such as roles and users. However, since the focus of this article is on removing and registering components, it covers only the Packages collection.
Connecting to the server and getting a list of packages requires only four lines of code:
'Create the catalog object and connect Set oCatalog = CreateObject("MTSAdmin.Catalog.1") oCatalog.Connect "" 'Create and populate the packages collection Set oPackages = oCatalog.GetCollection("Packages") oPackages.Populate
Once you’ve connected and populated the packages collection, you can begin to do something useful. First, take a look at removing a component. The basic principal is straightforward enough: iterate through the component collections until you find one or more components matching the DLL filename in which you are interested. The devil is in the details, however, and there are a few items worth pointing out.
Multiple DLL files with the same filename can be registered in different packages on the same server. This is perfectly legal, of course, because each component has a different Class Id. Thus, using the Class Id seems to be the best way to identify a componentbut is it? In a development environment, binary compatibility is broken from time to time. When this happens, your component’s Class Id changes, making it a less than ideal method of identification. Most developers do not rename their DLL file, however, so stick with the filename. So how do you solve the problem of multiple components sharing filenames?
As you can see in Listing 1, the RemoveComponents method has a PackageName parameter. If you know the name of the package in which your component resides, passing the package name will eliminate the problem. As an added benefit, your component is removed more quickly because only the specified package is searched. If your project uses multiple packages, but conforms to some type of naming convention, you can also use a wildcard in the package name. So, passing “ACME*” as the package name searches only those packages beginning with “ACME.”
When the package matches the specified name or wildcard, iterating through its components is fairly straightforward. Begin by asking the package object to populate a Components collection.
Set oComponents = oPackages.GetCollection _("ComponentsInPackage", oCurrentPackage.Key)oComponents.Populate
After populating the components collection, you move through the components backwards. This preserves the array indexes as we delete components. Next, call the component objects’ “Remove” method. Once you’ve looped through all the components, tell MTS to apply your changes by calling the “SaveChanges” method.
For lngIndex = oComponents.Count - 1 To 0 Step -1 Set oCurrentComponent = oComponents.Item(lngIndex) . . . If strCurrentDLL = strDLLToRemove Then oComponents.Remove lngIndex UNC = PathToUNC(oCurrentComponent.Value("DLL")) PackageName = oCurrentPackage.Name RemoveComponents = True End IfNextIf RemoveComponents = True Then oComponents.SaveChanges Exit ForEnd If
One last item to note is that the UNC path of the DLL file is returned to the calling process. This will be useful when you write your add-in. Once a component has been removed from a package, copy the new version to this UNC path and register your latest and greatest.
Registering a component uses basically the same code you’ve already seen to locate the proper package object. Once the package is located, retrieve a reference to a “ComponentUtil” object, which will allow you to install a component.
Set oComponentUtil = oComponents.GetUtilInterface oComponentUtil.InstallComponent DLLFilename, "", ""
This Is How It Works
Now that you have the code necessary to remove and install components, you’ll use an add-in to call those routines. The AutoRegister project (included) can be used as a starting point for your own add-in. Most of the code is the boilerplate code generated by VB when you chose to create an add-in project. Only the event handler for the Register button is discussed here.
|Figure 2: AutoRegister in action.|
Click the register button, and the AdminMTS component is instantiated on the specified server. If the RemoveComponents method is successful, the new DLL file copies to the appropriate location, and the InstallComponents method is called (see Figure 2). The file is copied using the Windows API SHFileOperation function. This gives you some additional features at no cost, such as prompting the user for additional authentication information if necessary, and a nice animation while the file copies.
I have not mentioned security at all, because there are so many different ways security can be enforced. Some development environments use the same security for their development server as their production server. Some leave the development server nearly wide-open and only lock down the production boxes. Whatever your paradigm, be aware that your developers need sufficient rights to copy their new DLL files to the server. Also, the AdminMTS component should be configured to run using an account with sufficient permissions to remove and install MTS components.
With the introduction of Windows 2000 and COM+, Microsoft has created the COM+ 1.0 Admin Type Library to interact with the transaction server. Fortunately, some backward-compatibility was preserved and the code in this article should work on a Windows 2000 server or a Windows NT 4 server. However, if you are developing tools to automate MTS administration and are in a Windows 2000 only environment, I recommend that you use this new interface.
There are a hundred things you can do to integrate the add-in supplied here with your development process. At my place of employment, a list of servers is presented to the user rather than a freeform textbox. The package prefix is automatically chosen since we adhere to certain naming standards. And a record is written to a SQL table every time a component is registered. It is much easier to pin down compatibility issues when you know what components have changed recently. The possibilities are endless. If you customize the add-in in a way that significantly improves your development cycle, I would like to hear about your experiences via e-mail.