|
Introduction
Many times developers have had to manually configure MTS using the MTS Explorer, as a part of deploying components. Now there is a way to do all that from a script, plus build an executable out of it, and include it as part of your setup program.
In this article, I will show how package deployment and maintenance in the Microsoft Transaction Server (MTS) Explorer can be automated using the scriptable administration objects. I have created a COM object using Visual Basic 6.0 that uses MTS's various catalogs and collection objects to administer packages, install components, and set properties. Let's step through the source code to understand how this is done.
Scriptable Administration Objects
The scriptable administration objects can be used to install, delete, and update properties for packages and components. They can also be used to configure clients, roles, and export packages. These objects are derived from the IDispatch interface, so you can use any automation language to develop your package, such as Microsoft Visual Basic 5.0, Microsoft Visual C++ 5.0, Microsoft Visual Basic Scripting Edition (VBScript), and Microsoft JScript. (JScript is Microsoft 's extended implementation of ECMAScript (ECMA262), an international standard based on the Netscape 's JavaScript and Microsoft's JScript languages.)
Idispatch, the interface for late-bound clients and Object Linking and Embedding (OLE) automation, does not require you to know the exact entry in the vtable of a COM object. IDispatch also supports introspection or the enumeration of interface characteristics at runtime. IDispatch is also not as fast as vtable-bound COM clients.
For a summary of what can be achieved using these objects, refer to:
http://msdn.microsoft.com/library/psdk/mts20sp1/building_9mk4.htm
The MTS Configurator and .INI File
The MTS Configurator reads a configuration file to access the packages to be created and update properties. It also installs components under that package, using a dynamic link library (DLL). Properties for the component are also updated. All of these tasks will be executed on the local machine where MTS is installed. (See the Where to Go from Here section at the end of this article for a list of functionality that you can enhance.)
The configurator class does its work by reading an .INI file that is specified by the host application. This file has to be in a specific format and contains the list of packages/components to be installed and properties to be updated. Let us look at the anatomy of this .INI file. You may open the .INI file (mtsconfig.ini) that is included with the download.
A sample .INI is shown below:
[List]
Second Sample
First Sample
[First Sample]
DllFileName=c:\mtsconfig\samples\sample1.dll
ReCreate=Y
Description=This is the first SAMPLE
Authentication= 4
ShutdownAfter= 9
RunForever=Y
SecurityEnabled=Y
Activation=Local
Changeable=Y
Deletable=Y
ComponentList=Sample1.MyClass1
[Sample1.MyClass1]
Description=The ONE and only class of the first sample.
Transaction=Requires New
SecurityEnabled=N
[Second Sample]
DllFileName=c:\mtsconfig\samples\sample2.dll
ReCreate=Y
Description=This is the second Sample. It has two components.
Authentication= 6
ShutdownAfter= 2
RunForever=N
SecurityEnabled=N
Activation=Inproc
Changeable=N
Deletable=N
ComponentList=Sample2.MyClass1;Sample2.MyClass2
[Sample2.MyClass1]
Description=The first class of the second sample.
Transaction=Not supported
SecurityEnabled=N
[Sample2.MyClass2]
Description=The second class of the second sample.
Transaction=Required
SecurityEnabled=Y
To create the .INI file:
· - List the names of the packages that you want to configure, under the section “List.” Note that packages will be created in MTS Explorer exactly as you specify them here.
·
- Create a separate section for every package specified under the List section. For example, the above sample has two separate sections named “First Sample” and “Second Sample.” These are exactly the same as the package names specified under the List section List.
·
- For each of these sections, create key-value pairs to specify properties that need to be set. A brief explanation of these key names is given below:
·
|
Key Name
|
Meaning |
|
ReCreate |
Specifies
whether the package has to be deleted and created again.? Note that this is not an MTS package
property, rather it is used by the MTS Configurator before deleting the package.
Datatype:
Char(1)
Default
Value: N
Possible
Values:
Y – Yes, delete, and re-create
N – No, do not delete or re-create, only update the
properties |
|
Description |
Describes
the package. Description fields hold a maximum of 500
characters.
Data
Type: String
Default
value: None
Access:
Read/Write |
|
Authentication |
Sets the authentication level for calls. Possible values are 0 through 6,
which correspond to the Remote Procedure Call (RPC) authentication
settings.
Data
Type: Long
Default
value: 4 |
|
ShutdownAfter |
Sets
the delay before shutting down a server process after it becomes idle.
Shutdown latency ranges from 0 to 1440 minutes.
Data
Type: Long
Default
value: 3 |
|
RunForever |
Enables
a server process to continue if a package is idle. If value is set to "Y",
the server process will not shut down when left idle. If set to "N", the
process will shut down according the value set by the ShutDownAfter
property.
Data
Type: String
Default
value: N |
|
SecurityEnabled |
Checks
the security credentials of any client that calls the package, if value is set
to "Y."
Data
Type: String
Default
value: "N" |
|
Activation |
Sets
the package level activation property to either "Local" or "Inproc."
. The Local setting determines that
objects within the package will run within a dedicated local server
process. A package running under the Local activation setting is a "server
package"." The Inproc activation setting means objects
run in their creator’s process. A package running under the Inproc
activation setting is a "library package."
Data
Type: String
Possible
values: "Local," "Inproc"
Default
Value: "Local" |
|
Changeable |
Sets
whether changes to the package settings, or those of its components, are
allowed (either programmatically, or through the MTS UI).
Data
Type: String
Default
Value: Y |
|
Delete Table |
Sets
whether the package or its components can be deleted (either
programmatically, or through the MTS UI).
Data
Type: String
Default
Value: "Y" |
|
ComponentList |
The
list of components to be configured under this package. Note that this is
not a MTS package property, rather it is used by the MTS Configurator to update
component properties. This is a semicolon that seperates rated list of component IDs. Example:
ComponentList=Sample2.MyClass1;Sample2.MyClass2 |
|
DllFileName |
The
DLL that has the above components. Give the complete path and file name of
the DLL.
Example:
DllFileName=c:\mtsconfig\samples\sample1.dll |
- Create separate sections for each of the components listed against the key name ‘”ComponentList.” Each such section for a component has the following key-value pairs. See sample .INI file above.
|
Key name
|
Meaning
|
|
Description
|
Describes the component. Description fields hold a maximum
of 500 characters.
Data Type: String
Default value: None
|
|
Transaction
|
Determines how a component supports transactions. Must be
one of the following transaction settings: "Required,", "Requires New,", "Not
Supported,", or "Supported."
Data Type: String
Default value: "Not supported"
|
|
SecurityEnabled
|
Checks the security credentials of any client that calls
the component, if
value is set to "Y."
Data Type: String
Default value: "Y"
|
Some important notes on the .INI file:
- Make sure that the key names are spelled EXACTLY as shown above or in the sample file
- Make sure that the value for the keys is spelled EXACTLY as specified in the above tables.
Now that we understand how to create the .INI file, let’s go through a comprehensive explanation of how the MTS Configurator has been developed and see how it uses the .INI file to get information on what to configure. I used Microsoft Visual Basic 6.0 in development.
Open the VB project, as follows:
- Unzip the self-extracting EXE file that is included (see the Downloadable Files section).. Three folders will be created: MTSConfig, MTSTester, and Samples.
- Open the VB project named mtsconfig.vbp (Of course, you need to have Microsoft Visual Basic 6.0 loaded.)
The project has two classes:
Configurator
The configurator uses the methods on the Catalog, CatalogObject, and CatalogCollections objects to automate basic administration functionality. To do this, you must configure your Visual Basic project to reference the MTS administrative type library (MTSAdmin type library). To reference the MTSAdmin type library, select the References option from the Visual Basic Project toolbar, then browse the available reference files for the “MTS 2.0 Admin Type Library.”
Utils
Utils is a support class that has a list of functions to read a configuration settings (.INI) file. This class is private and is instantiated only by the configurator class.
The configurator class uses three global variables that will be used by all the functions and subroutines of the class. These variables are:
Dim ObjUtils As New utils ‘ Handle to the support class Utils
Dim packages As Object ‘ To hold the handle to all the packages in the catalog
Dim objFile ‘ Handle to the log file
The configurator starts its jobs when a call is made to the subroutine Initialize. This subroutine takes in a string argument that specifies the .INI file to use. The .INI file we discussed a few paragraphs earlier is the same one that needs to be passed here.
There are a couple of things to note about the Initialize subroutine:
A log file is created to log messages about the progress or errors as the MTS objects are configured. The log file is hard-coded as mtsconfig.log. The location of this file is the same as the location of the .INI file:
' Get the path of the .INI file, to create a log file
strPath = Mid(strIniFileName, 1, InStrRev(strIniFileName, "\"))
strLogFile = strPath & "mtsconfig.log"
The vital step to MTS administration by script is to create a handle to the catalog object. The catalog object is the MTS data store that maintains configuration information for components, packages, and roles.
Set catalog = CreateObject("MTSAdmin.Catalog.1")
Next, get a “Packages” collection object by calling the GetCollection method. The Packages collection returns without retrieving any data from the catalog so that the collection will be empty upon return from the GetCollection method. Populate the Packages collection to read in all packages. This objects will be referenced by the other functions and subroutines of the class.
Set packages = catalog.GetCollection("Packages")
packages.Populate
After establishing a handle to the Packages collection., we need to read the .INI file and start configuring the packages listed under the List section. This job is done by the getkeyvaluepairs function of the Utils class. The variable ObjUtils is a global variable of the Utils class.
' Get the list of packages/DLLs that need to be configured.
strList() = ObjUtils.getKeyValuePairs("List")
' Loop through the list of components to be configured.
lngListTotal = UBound(strList)
' Configure each of the listed packages.
For i = 0 To lngListTotal
intReturnStatus = ConfigureMTSPackage(strList(i))
Next
The ConfigureMTSPackage function above is actually a public function that you can call directly without calling the RegisterPackages function. (See the Tricks and Pointers section for a brief discussion.) The ConfigureMTSPackage function configures one package at a time. Let’s probe into it to see what it does.
Firstly, check the .INI file if the package needs to be re-created. The key that specifies this option is “ReCreate”’ under the section that has the same name as that of the package name. If you want to simply update some properties, the value for the key would be ‘N.’ If the value has been set to ‘Y,’ then call a function CreateMTSPackage that will try to re-create the package.
strValue = ObjUtils.getValueForKey(strPackageName, "ReCreate")
blnRecreate = IIf((strValue = "" Or strValue = "N"), False, True)
' If it is OK to re-create the package, just do it!!
If blnRecreate Then
intReturnStatus = CreateMTSPackage(strPackageName)
Else
objFile.WriteLine "Skipping re-creation of the package: '" + strPackageName + "'"
End If
The CreateMTSPackage function loops through all the packages in the collection to find a name match. If the package is available in the catalog, then it checks to see if the current version of the package can be deleted. An existing package can be deleted if the check box “Disable Deletions” is unchecked in MTS Explorer. If it is deletable, the package is deleted and the changes are saved.
' Re-populate the Packages collection, since additions/deletions need
' to be reflected.
packages.Populate
n = packages.Count
For i = n - 1 To 0 Step -1
If packages.Item(i).Value("Name") = strPackageName Then
If packages.Item(i).Value("Deletable") = "N" Then
' Log a warning!
objFile.WriteLine vbCrLf + "WARNING: The " +_
"current version of the package '" + _
strPackageName + _
"' is set to Disable Deletion in MTS " + _
"Explorer. Cannot delete it."
intReturnStatus = -1
GoTo Exithere
End If
packages.Remove (i)
' However, the removal of the package is not applied to the catalog until the SaveChanges method is called.
packages.SaveChanges
Exit For
End If
Next
Add the package to the collection by calling the Add method. Set the name of the package, and save the changes to the catalog.
Dim newPack As Object
Set newPack = packages.Add
' Update the Name property
newPack.Value("Name") = strPackageName
' Call the SaveChanges method to save the new package to the catalog.
packages.SaveChanges
Now, irrespective of whether the package was re-created or not, the properties have to be updated. In order to do this, you would need a handle to the package object that is residing in the Packages collection. A generic function getPackageObject does this for you.
' Get a handle to the package object based on the current package name
Set ThisPackage = getPackageObject(strPackageName)
Properties for a package can be updated if the current version in the MTS Explorer allows it, meaning if the check box “Disable Changes” in MTS Explorer is unchecked. The next piece of code checks for this before starting to make any changes:
strValue = ThisPackage.Value("Changeable")
If strValue = "N" Then
objFile.WriteLine vbCrLf + "WARNING: Properties for the " +_
"package:'" + strPackageName + "' cannot be changed." + _
" The current version in MTS Explorer has been set " + _
"to Disable Changes. Components for this package " + _
"will not be created or their properties changed."
intReturnStatus = -1
GoTo Exithere
End If
If the properties can be updated, then read the values for each property from the .INI file. Note that each property is a key under the section for the package. An example showing the code to update the property ShutdownAfter is shown below. Note that the code takes a default value if none is specified. Use this link for a complete listing of these properties. Note that only the properties that are Read/Write can be updated from script. http://msdn.microsoft.com/library/psdk/mts20sp1/adminref_0isz.htm
strValue = ObjUtils.getValueForKey(strPackageName, "ShutdownAfter")
lngValue = IIf((strValue <> ""), CLng(strValue), 3)
ThisPackage.Value("ShutdownAfter") = lngValue
After the properties have been updated, they need to be saved.
packages.SaveChanges
Now that the package is ready, we must configure the components under this package. This is taken care of the by the ConfigureComponents function. This function is called from the ConfigureMTSPackage function discussed above.
Configuring components under a package follows the same logic as that of configuring a package. A package has to be changeable (the Disable Changes check box needs to be unchecked in MTS Explorer) in order to configure components. This check is incorporated in the ConfigureComponents function. If the package is changeable, then we need to find out which components needs to be configured. This information is obtained from the .INI file. The list of components to be configured are listed in a semicolon-separated list for the key ComponentList for the package under which these need to be created. This list is read from the .INI file, and the individual component names are split so they can be used later.
' Get the list of components as specified in the configuration file
strComponentList = ObjUtils.getValueForKey(strPackageName, _
"ComponentList")
astrComponents = Split(strComponentList, ";")
lngTotal = UBound(astrComponents)
Before the components can be configured, we need to find out what components are already available for the package. In order to do this, call the GetCollection method to retrieve the ComponentsInPackage collection. Supply the key of the current package as a parameter, and then populate the object.
Set Components = packages.GetCollection("ComponentsInPackage", _
ThisPackage.Key)
' Fill the ComponentInPackages collection using the Populate method
Components.Populate
n = Components.Count
If there are no components currently available in the collection, then either the package has been re-created or it is so by design. In this regard, all the components found in the accompanying DLL file will be installed. A single component cannot be installed in the current version of the MTSConfigurator. Components can be installed by using a component utility object. Call the GetUtilInterface method to get the component utility object. Call the InstallComponent method, passed in a string containing the name of the DLL of the component to be installed. This name of this file is read from the .INI file from the key DLLFileName. If the component does not have an external type library or a proxy-stub DLL, pass in empty strings as the second and third arguments. Note that you do not have to call the SaveChanges method after installing a new component. All components contained in a DLL will be installed by this method, and are immediately written to the catalog.
' This object is used to install components.
Dim util As Object
Set util = Components.GetUtilInterface
' IF there are no components, that means that the package was re-created,
' So the components have to be re-created
If n = 0 Then
' Get the name of the DLL to be registered
strDllFileName = ObjUtils.getValueForKey(strPackageName, _
"DllFileName")
If strDllFileName = "" Then
objFile.WriteLine vbCrLf + "WARNING: DLL file name not specified for installing the components."
End If
util.InstallComponent strDllFileName, "", ""
' Call the GetCLSIDs method to get the CLSIDs of the
' components installed.
util.GetCLSIDs strDllFileName, "", installedCLSIDs
' Call the PopulateByKey method to read back the components just
' installed. Note that the components installed into the package
' via the InstallComponent method are not visible in the
' collection until the Populate or PopulateByKey method is called ' to read the data from the catalog.
Components.PopulateByKey installedCLSIDs
n = Components.Count
End If
Now, the properties for the components have to be updated. In order to do this, loop through the list of components found in the .INI file and find a match for the name (ProgID) in the collection. If a match is found, read the value for the property (from the section with the same name as that of the ProgID) of the component and update it. The following code shows how the Transaction property is updated.
' Iterate through the list found in the .INI file.
For j = 0 To lngTotal
' Iterate through the components collection to find a match
For i = n - 1 To 0 Step -1
Set ThisComponent = Components.Item(i)
' Get the properties for the component
If ThisComponent.Value("ProgID") = astrComponents(j) Then
' Determine how a component supports transactions.
' The default is 'Not supported'
strValue = ObjUtils.getValueForKey(astrComponents(j), _
"Transaction")
strValue = IIf((strValue <> ""), strValue, _
"Not supported")
ThisComponent.Value("Transaction") = strValue
End If
Next
Next
Call the savechanges method to update all changes to the catalog.
Components.SaveChanges
That’s it folks. One package and its components are configured. The RegisterPackages function loops through the list of packages found under the List section in the .INI file and configures each of the packages and respective components in the same way.
Tricks and Pointers
There are a couple of tricks that you can do to the MTSConfigurator component.
- You may have separate sections for packages that are not listed under the List section. There is no harm here, but beware that the configurator class will configure ONLY those packages that are listed under this section. This way, you can create a .INI file at one shot but configure them according to your priority.
- Notice that the ConfigureMTSPackage function is public. Call this function directly to configure a single package without having to loop through all the packages listed under the List section. Then be sure to create a section for that package and one section each for the components under that package.
Testing MTS Configurator
I have included two sample DLLs in the self-extracting EXE and a tester project written in Microsoft Visual Basic 6.0. If you haven’t yet extracted this, please do so now. Open the VB project named mtstester.vbp. Register the two DLLs (sample1.dll and sample2.dll) included with the download.
Downloadable Files
I have zipped up the following files that you can download to use, enhance, and test the MTSConfigurator component:
- MTS Configurator DLL (mtsconfig.dll)
- VB project and class files used to generate the DLL (see in the folder MTSConfig)
- A tester application to show how to use the DLL (see in the folder MTSTester)
- A sample log file that is generated (mtsconfig.log)
Download: http://15seconds.com/files/991118.zip
Where to Go from Here?
There is a lot more you can do to make this COM object fully functional. Here are some things that can be incorporated:
- Installing a prebuilt Package
- Administer a package on a remote computer
- Configuring a role
- Exporting a package
- Configuring a client to use remote components
Note that you can use the scriptable administration objects to automate any task that is done in the MTS Explorer. Use the links to find out how:
- The MTS Administration Objects
http://msdn.microsoft.com/library/psdk/mts20sp1/scriptable_4kj7.htm
- Automating MTS Deployment
http://msdn.microsoft.com/library/psdk/mts20sp1/building_9mk4.htm
- Automating MTS Administration with Visual Basic
http://msdn.microsoft.com/library/psdk/mts20sp1/scriptable_2n8u.htm
- Automating Advanced MTS Administration with Visual Basic
http://msdn.microsoft.com/library/psdk/mts20sp1/scriptable_363y.htm
Use the VB project and class files that have been included to enhance or modify the COM object to suit your needs. Build the DLL, and you are ready to go!
About the Author
Anil has been a systems integrator and consultant for five years in
transaction intensive client-server environments. In the recent past, he has
been focussing on the areas of design and implementaion of business
components and distributed transactions using Microsoft Technologies, whilst
also developing internet applications in ASP. His other interests include
learning new languages, woodworking and watching the History channel!! He
can be reached at linans@hotmail.com
References
I don’t need to tell you what you can find in MSDN online, if you know what you are looking for. I worked my way up based on the information I found in these two articles:
Automating MTS Deployment
http://msdn.microsoft.com/library/psdk/mts20sp1/building_9mk4.htm
Visual Basic Sample Application for Automating MTS Administration
http://msdn.microsoft.com/library/psdk/mts20sp1/scriptable_7e0e.htm
|