Migrating Visual J++ COM+ Components to Visual J# and Enterprise Services

Migrating Visual J++ COM+ Components to Visual J# and Enterprise Services

Visual Studio .NET 2003
 

Doug Purdy
Visual Studio Team
Microsoft Corporation

August 2002

Summary: This article provides the information you need to successfully move your COM+ components to the .NET Framework using Visual J# .NET. This article covers what COM+ support is currently found in the Microsoft VM and Visual J++ and discuss the equivalent support found in the .NET Framework and Visual J# .NET. (10 printed pages)

Contents

Introduction
Mapping to EnterpriseServices and InteropServices
   @com.register and @com.transaction Attributes
   ServicedComponent and TransactionAttribute
   GuidAttribute and ProgIdAttribute
   Registration
   Strong Names
   Global Assembly Cache and Regsvc.exe
Migrating Common COM+ Features
   com.ms.mtx.Context Class
   com.ms.mtx.SecurityCallContext and com.ms.mtx.SharedPropertyGroupManager Classes
   com.ms.mtx.IObjectControl Interface
Conclusion

Introduction

Visual J++ is an incredible environment for developing COM+ applications. This toolset, in concert with the power of the Java language and the Microsoft virtual machine (VM), enables developers to rapidly build powerful distracted application. However, with the advent of the .NET Framework and the eventual phase-out of distribution of the Microsoft VM, many developers find themselves looking for the best path to migrate their Visual J++ COM+ components to the .NET Framework.

Mapping to EnterpriseServices and InteropServices

The .NET Framework contains two namespaces that are especially useful to those migrating an existing Visual J++ COM+ component. The System.Runtime.InteropServices namespace (hereafter InteropServices) provides types that are responsible for interoperation between the COM runtime and the common language runtime. This namespace can be viewed as a direct replacement for the com.ms.com package and most of the @com compiler directives in Visual J++.

The System.EnterpriseServices namespace (hereafter EnterpriseServices) consists of types that expose the services provided by COM+. This namespace can be viewed as a direct replacement the com.ms.mts package that is included with the Microsoft VM and the @com.transaction compiler directive in Visual J++. The most significant differences between using these namespaces with Visual J# and using the existing support in Visual J++ are the manner in which you define and register a class to act as a COM server and take advantage of COM+ services.

@com.register and @com.transaction Attributes

Within Visual J++, a Java-language class is exposed as a COM+ component by placing the @com.register and the @com.transaction attributes on the class declaration. The @com.register attribute defines the following elements:

  • Coclass GUID
  • Type library GUID
  • ProgID for the component

The information provided by this attribute is inserted into the Windows Registry to expose the Java-language class as a COM server. The @com.transaction attribute defines the transaction requirements for instances of the class. This value can be any of the transaction support levels provided by COM+ (Required, Requires New, Supported, or Not Supported).

The following class declaration shows the proper use of these attributes:

/**
 * @com.register ( clsid=FE9C2CAA-6232-4169-8911-B593D4E706DA, 
typelib=943A02F9-72C1-45C3-B057-D758357B4B4E, progid="VJServer.Server")
 * @com.transaction(required) 
*/
public class Server {}

Note that there are several different ways of exposing a Java-language class as a COM+ component. The use of @com.register and @com.transaction attributes simply represents one of the most common scenarios.

ServicedComponent and TransactionAttribute

Within the .NET Framework, classes must extend from the EnterpriseServices.ServicedComponent class to be exposed as a COM+ component. There are other ways of doing this, but this is the recommend way of taking advantage of the COM+ services within the .NET Framework. The requirement for this common superclass is driven by the need for interception. By extending from ServicedComponent, method calls to instances of your class can be intercepted by the common language runtime. The common language runtime can then invoke instances of classes in the EnterpriseServices namespace to provide the COM+ services you are familiar with.

To programmatically define the transaction support level, the EnterpriseServices.TransactionAttribute must be added to your class declaration. As with @com.transaction in Visual J++, this attribute defines the transactional context in which instances of your class must be activated. The main difference between these attributes is the type-safe nature of attributes in the .NET Framework and the new attribute syntax in Visual J#.

The following class declaration shows how the Visual J++ Server class should be represented in the .NET Framework using Visual J#:

import System.EnterpriseServices.*;

/**@attribute Transaction(TransactionOption.Required)*/
public class Server extends ServicedComponent {}

Note the use of the TransactionOption enumeration, which sets the transaction support level within the TransactionAttribute. This enumeration defines the transaction levels in a type-safe manner. In addition, note the omission of the Attribute portion of the TransactionAttribute name. The Visual J# compiler does not require you to include the full name of the attribute you are adding to a class.

GuidAttribute and ProgIdAttribute

To set the coclass CLSID and ProgID, use the InteropServices.GuidAttribute and InteropServices.ProgIdAttribute attributes. These two attributes serve the exact same purpose as the @com.register attribute in Visual J++. You specify the GUID and ProgID that should be used to expose your class as a COM+ component to COM clients. The following code shows the addition of these attributes to the Server class:

import System.EnterpriseServices.*;
import System.Runtime.InteropServices.*;

/**@attribute Transaction(TransactionOption.Required)*/
/**@attribute Guid("FE9C2CAA-6232-4169-8911-B593D4E706DA")*/
/**@attribute ProgId("VJServer.Server")*/
public class Server extends ServicedComponent {}

If you omit these attributes, the .NET Framework will generate them for you when you register the assembly with COM+. That said, if you intend on supporting existing COM clients, you should you explicitly set these attributes. This is especially true if you plan on replacing your existing Visual J++ COM+ components in a seamless fashion.

If you only intend on supporting .NET Framework-based clients, you can rely on the default generation. .NET Framework clients use the newobj intermediate language instruction (new in Visual J#) to create instances of your class. They do not use the legacy CoCreateInstance API call used by COM clients.

Registration

At this stage, you know how to create a Visual J# class that mirrors the Visual J++ COM+ component class. It has the same ProgID, the same CLSID, and the same transaction support level. The only remaining task is to discuss the registration differences. With that complete, you will have learned the essence of how to migrate a Visual J++ COM+ component.

Assuming that you are using the COM DLL project template, you are aware that Visual J++ will create a COM DLL at that end of the build process. This process also generates a type library (that will be embedded in the DLL) and the registration of this DLL with the Windows Registry (using an instance of the DllRegisterServer export, which Visual J++ automatically generated). From that point on, the generated COM DLL is just like another COM server. You can copy it to another machine and run regsvr32.exe (which calls DllRegisterServer) and then use the COM server from this new location.

While this operation is sufficient to register the COM server, you still need to register the component with the COM+ catalog. This can be accomplished a variety of ways, ranging from using the COM+ Administration API to dragging and dropping the DLL from Windows Explorer into the Component Services MMC Snap-In. Regardless, unless the component is registered with COM+, it will not be able to take advantage of any of the COM+ services.

In the same way, a Visual J# class will also need to be registered with both the Windows Registry and the COM+ catalog. However, Visual J# does not emit a COM DLL after compiling a class. Instead, it produces a .NET assembly. While this file may have the same extension as COM DLL, it is not a COM component. Also, although it shares the same Portable Executable (PE) file format, this file does not contain the critical DllGetClassObject export required from a COM DLL.

The .NET Framework SDK provides a tool that serves the same function as regsvr32.exe. This tool, regasm.exe, inspects the GUID and ProgID attributes found on classes in a given .NET assembly and uses this information to create the correct entries in the Windows Registry. While this addresses the first part of the registration problem, it does not handle the registration with COM+. Unfortunately, this process is not as simple as dragging and dropping a file into the MMC. To address the second stage of registration, we first need to digress into one of underlying features of the .NET Framework.

Strong Names

The common language runtime uses a very powerful type loader, which provides support for class versioning. This is a piece of functionality that is not available in the Microsoft VM or any other Java-language virtual machine implementation. In short, every .NET assembly contains version information embedded within it. This information is used to ensure that consumers of classes in the assembly get the right version and not just whatever class or JAR file that is found first while searching the classpath.

While this is better than simply using the class name (as in the Java language), it still suffers from the same fundamental problem: There is nothing to prevent someone from coming along and replacing an assembly with another one with the same name and version. The .NET type loader would have no idea that such a switch had occurred and the application would not work as expected (or worse, the new assembly could be used to exploit the system in some way).

The solution to the problem in the .NET Framework is to uniquely name an assembly. This is known as giving an assembly a strong name. A strong name consists of the following:

  • Assembly name
  • Version
  • Culture (if provided)
  • A public key
  • A digital signature

To create a strongly named assembly, you need to have a cryptographic key pair (public and private key). The public key is embedded in the assembly file as part of the strong name. The private key is then used to sign a hash of the assembly. The resulting digital signature is also embedded in the assembly file. The .NET Framework SDK includes a tool, sn.exe, which will create a cryptographic key pair that can be used to create a strongly named assembly. Use the –k option and specify a file name to write the key pair into (using the .snk extension is the standard).

There are two items of note related to this key file:

  • Make sure to keep it secure. Public keys can be used to grant security permissions during runtime. If someone has access to your key pair, they could use this to build assemblies that could run at the same level of trust that your assemblies execute under.
  • You do not have to generate a new cryptographic key pair for every assembly you want to give a strong name. Instead, you can use the same key pair for all assemblies you create. For example, all the assemblies in the .NET Framework utilized the same key pair. These means that they all have the same public key as part of their strong name and were signed by the same private key. You may want to take the same approach with assemblies you produce.

Once the .snk file has been created with sn.exe, generating the assembly with a strong name is a simple operation. All you need to do is add two new attributes to the class file and then rebuild the project. The following code shows these additions:

import System.EnterpriseServices.*;
import System.Runtime.InteropServices.*;
import System.Runtime.CompilerServices.*;
import System.Reflection.*;

/**  @attribute Transaction(TransactionOption.Required)
 *   @attribute Guid("FE9C2CAA-6232-4169-8911-B593D4E706DA")
 *   @attribute ProgId("VJServer.Server")
 *    @assembly AssemblyVersion("1.0.0.0")
 *    @assembly AssemblyKeyFile("key.snk")
*/
public class Server extends ServicedComponent {}

The System.Reflection.AssemblyVersionAttribute is used to manually set the assembly version number. If you do not provide this attribute, Visual J# will generate it for you. The System.Runtime.CompilerServices.AssemblyKeyFileAttribute contains the relative (or absolute) path to the key file generated by sn.exe utility. Once you add these attributes, you just need to rebuild the project to have a strongly named assembly.

Note the new syntax for defining the assembly attribute. Because these attributes apply to the entire assembly, use the @assembly declaration rather than @attribute.

It is worth pointing out that when you create a new Visual J# project, these attributes will automatically be added to the project in a separate file called AssemblyInfo.jsl. This is a standard convention that all Microsoft languages share (C# uses AssemblyInfo.cs and Visual Basic .NET uses AssemblyInfo.vb). This file contains all the attributes associated with a given assembly rather than a specific class or interface. For the sake of brevity, the above example does not make use of this file. As long as these attributes are found in some source file within the project, Visual J# will produce a strongly named assembly.

Global Assembly Cache and Regsvc.exe

So now that you have an assembly with a strong name, we return to the topic of registration. You may be wondering why we had to go into such great detail about strong names, but the reasoning is fairly simple. To register a .NET assembly with the COM+ catalog, it must have a strong name. Because the COM+ catalog is a machine-wide resource, a unique name for every assembly is an absolute must.

The .NET Framework provides two separate ways of registering a .NET assembly with the COM+ catalog: dynamic and manual registration. Both these methods will only work on strongly named assemblies that contain classes that extend from EnterpriseServices.ServicedComponent.

Of these two, the dynamic registration mechanism is the simplest, but the most limited. When a client creates an instance of a class that extends ServicedComponent (in your case, the Server class), the runtime verifies whether the class is registered with the COM+ catalog. If not, the runtime automatically registers the assembly with both the Windows Registry and the COM+ catalog. There are two warnings with this approach:

  • Assemblies that use dynamic registration are not usable by COM clients.
  • The identity of the thread that principally creates the new instance of the ServicedComponent class must be an administrator on the machine hosting the component.

Manual registration involves additional overhead, but it is more flexible. This method eliminates the major issue with dynamic registration: the lack of support for COM clients. Nevertheless, this process still requires that an administrator account on the local machine be used to perform the registration.

To manually register a .NET assembly, you need to use two additional utilities found in the .NET Framework SDK: gacutil.exe and regsvc.exe.

Gacutil.exe is used to install your assembly into the global assembly cache (GAC). The GAC is the equivalent of the %WinDir%\Java\Classes directory, that is, a directory that contains classes accessible to the classloader. The GAC serves the same purpose. If an assembly is installed into the GAC, it is available to any application running on the system.

Note that while you can navigate to <WinDir>\Assembly to view the assemblies in the GAC, the GAC is really composed of many different directories that are virtualized into the Windows Explorer view. This is the reason for the separate utility to install assemblies into GAC, rather than just copying the assembly into this directory.

Once the assembly has been placed in the GAC, the last step is to register the assembly in the COM+ catalog using the regsvc.exe utility. Regsvc.exe takes the name of the assembly to be registered as a command line parameter. When invoked, the tool reads the metadata of the assembly, creates a COM+ application, and registers all classes that extend from EnterpriseServices.ServicedComponent with the COM+ catalog. By default, the name of the COM+ application is the name of the assembly (without the extension).

Migrating Common COM+ Features

With registration complete, you now know how to migrate a Visual J++ COM+ component to the .NET Framework and Visual J#. However, it is very likely that you will want to migrate far more complex COM+ components to the .NET Framework. To that end, we will proceed to walk through the migration of the most commonly used COM+ features:

com.ms.mtx.Context Class

Although COM+ provides a variety of useful services, the most prevalent feature is the declarative distributed transaction. While you know how to declaratively set the transaction support level (using the Transaction attribute) on the Visual J# class, you still need to discover how to vote on the outcome of the transaction. Within Visual J++, the com.ms.mtx.Context class is used to interact with the COM+ infrastructure during runtime. This class provides methods to control the well-known "happy" and "done" bits associated with the transaction context provided by COM+. The following code outlines the canonical usage of this class to vote on a transaction within Visual J++:

import com.ms.mtx.*;

   /**
    * @com.register ( clsid=FE9C2CAA-6232-4169-8911-B593D4E706DA, 
typelib=943A02F9-72C1-45C3-B057-D758357B4B4E, progid="VJServer.Server")
    * @com.transaction(required) 
    */
public class Server { 

   public void doWork() {
      
      try {
      
         // Do some database work here.
         // Set the happy and done bits.
         Context.setComplete();
         
      } catch (Exception ex) {
      
         // Handle the exception here.
         // Set the unhappy and done bits.
         Context.setAbort();
         
      }//try/catch   

   }//doWork

}//Server

Based on previous experience, you might presume that the EnterpriseServices namespace contains some sort of logical equivalent to the Context class in Visual J++. As the following code shows, EnterpriseServices.ContextUtil proves this presumption correct:

import System.EnterpriseServices.*;
import System.Runtime.InteropServices.*;
import System.Runtime.CompilerServices.*;
import System.Reflection.*;

/**   @attribute Transaction(TransactionOption.Required)
 *   @attribute Guid("FE9C2CAA-6232-4169-8911-B593D4E706DA")
 *   @attribute ProgId("VJServer.Server")
 *    @assembly AssemblyVersion("1.0.0.0")
 *    @assembly AssemblyKeyFile("key.snk")
*/
public class Server extends ServicedComponent {

   public void doWork() {
   
      try {
      
         // Do some database work here.
         
         // Set the happy and done bits.
         ContextUtil.SetComplete();
         
      } catch (Exception ex) {
      
         // Handle the exception here.
         
         // Set the unhappy and done bits.
         ContextUtil.SetAbort();
         
      }//try/catch
   
   }//doWork

}//Server

The similarity between the two is uncanny. In fact, assuming your code is similar to the previous Visual J++ example, you should be able to migrate your code to Visual J# with a find and replace operation. Change Context to ContextUtil and capitalize the first letter of the method names.

If you examine the EnterpriseServices.ContextUtil in greater detail, you will find most of the functionality of the com.ms.mtx.Context class (with slight modifications). While we could develop a method-by-method mapping, this exercise would be largely redundant as the differences are mostly naming changes. With that in mind, see the documentation for the ContextUtil members.

com.ms.mtx.SecurityCallContext and com.ms.mtx.SharedPropertyGroupManager Classes

The EnterpriseServices namespace contains direct replacements for the com.ms.mtx.SecurityCallContext and com.ms.mtx.SharedPropertyGroupManager classes. These new classes offer the same baseline functionality offered in the Visual J++, as well as access to new features within COM+. While this makes it easy to migrate your code to Visual J#, the best aspect of these replacement classes is the fact that their names are exactly the same as the original Visual J++ classes: EnterpriseServices.SecurityCallContext and EnterpriseServices. SharedPropertyGroupManager.

As with ContextUtil, we could provide a method-mapping table, but determining what changes are present between the two sets of classes is fairly trivial. A cursory examination of the SharedPropertyGroupManager class is all that is required to migrate your code.

com.ms.mtx.IObjectControl Interface

The com.ms.mtx.IObjectControl interface is implemented by Visual J++ COM+ classes that define context-specific activation and deactivation functions that will be invoked by the COM+ runtime (IObjectControl::Activate and IObjectControl::Deactivate). This interface also defines a method that can control pooling of instances (IObjectControl::CanBePooled). Implementing this interface is optional in Visual J++, but represents a common practice.

EnterpriseServices does not define a direct replacement for this interface; rather the ServicedComponent base class that you extend defines these methods. All you need to do is override these three methods in your subclass in order to duplicate the functionality found in Visual J++. Migrating code can be as simple as a copy-and-paste operation into a new Visual J# class (because the method signatures are the same).

Conclusion

As you have seen, migrating the existing Visual J++ COM+ component is a fairly simple exercise. Although we used a trivial example, Visual J# .NET and the InteropServices and EnterpriseServices namespaces provide all the baseline functionality required to successfully move your existing COM+ components to the .NET Framework in a rapid fashion. The most difficult aspect of this portion of a code migration is the effort required to understand the new design-time and runtime environment. With this article in hand, you should be well on your toward embracing the .NET Framework.

Show:
© 2016 Microsoft