Introduction
It was going a little boring for a while, and then I got something interesting to work on – "Classic ASP application needs to call a new .NET component(s)"; to be honest, the development of the .NET component is decided so that one day the application will be fully converted into .NET. In this article, I'll try to keep the discussion to the practical aspects of software development and keep it simple for developers to follow with a minimum distraction of buzz words.
Background
I have worked on similar assignments before with little difference; the front end application was in .NET, and the components were in C++/VB6. I started working on it right away, and tumbled many times on various issues. Everything is right if the end is right.
Writing the C# component was easy as walking on green grass bare foot, though log4net posed some issues with the config file; eventually, I moved to the ASP application to consume the C# component. Live Free or Die Hard; these are the two options, either use only .NET components, or use both with benefits of both, without the overhead of having to convert everything to .NET.
There are many options, and one should decide after checking out the performance, style, ease of development, and ease of debugging; believe me, different organizations have completely different styles, and still business goes as usual, i.e., there is no wrong or right way, and all depends on the business and the requirements.
One option is to use tlbexp to get the DLL ready for the ASP application. It needs another step of registering the DLL with regsvr32; the registration is required as you might remember, while working with the C++/VB projects.
The second option is to register and import with one command; regasm: this command creates the type library and registers the assembly. Registering the assembly is required as the VB6/C++ code registers with binary compatibility.
The third option is to create the service using regsvcs; regsvcs creates the type lib, registers the DLL, and creates the COM+ service. I like this as you can reap a lot of COM+ benefits like transaction queuing etc. Please refer to COM+ books for further reading, and as I mentioned before, I'll try to keep the theory out.
The first and the second options are really the same, so in a way, there are two options, and I started with the first, and liked the second later on, as I was working on this project. I have decided purely based on my knowledge, plus the need that the code is maintained by developers more familiar with VB than .NET.
I have many layers, and I was not sure if I needed to create a type library and/or register the lower layers; similarly, should I register the other assemblies referenced or used in the exposed assembly. I guessed that it's not required as eventually it will be running the .NET assembly; TLB is just a CCW wrapper, and doesn't include the rest of the assembled/binary code.
Let's Code
Ready? Let's jump right in....
The assembly needs to be consumed as a COM+ service, and here are the steps required to make it worthy. These steps are not sequential, and you can change the order as it fits your needs, but some of the steps depend on the previous steps.
Step 1: Add a reference to InteropServices
; this enables your component to be visible to COM and the Enterprise library, and provides core COM+ abilities. ServicedComponent
is part of the EnterpriseServices
namespace, and you will see in the next steps that the class must inherit ServicedComponent
. Add the following lines on the top of the class after the other using
statements.
using System.Runtime.InteropServices;
using System.EnterpriseServices;
By adding these references, you will get intellisense for the rest of the steps. ;-)
Step 2. Assign the name, description, activation, transaction, and the rest of the information for the assembly before the namespace tag. You can give any name, and by default, it will use the name of the project. There are a whole bunch of options available, and I have picked the bare necessities for my sloppy code. You only need to add these to classes you are exposing with COM+. Rest of the classes used by this class can stay clean, and these entries are not required again in the project.
[assembly: ApplicationName("JayTest")]
[assembly: Description("Jay Test business layer com+")]
[assembly: ApplicationActivation(ActivationOption.Server)]
[assembly: ApplicationAccessControl(false,
AccessChecksLevel = AccessChecksLevelOption.ApplicationComponent)]
namespace busLayer
{
The GUID can be created easily using the Create GUID tool in VS2005.
Step 3: GUID; as you remember, for binary compatibility, the GUID on the class is required to make the class have a unique class ID. Don't worry about ServicedComponent
as it is explained in the following steps.
[GuidAttribute("ADF0D549-D84B-422c-A15E-5B22C1E35FB5")]
public class JayClass :ServicedComponent
{
Step 4: Make the DLL COM visible. Of course, you need that, and place the following code in ApplicationInfo.cs. It is self-explanatory, and should be set as true
; if you don't do this, the assembly can't be used, and you will get an error message in the CreateObject
call.
[assembly: ComVisible(true)]
Step 5: ServicedComponent
: This topic can take the whole chapter or a book, but following the KISS principle, your COM+ exposed class must inherit ServicedCompnent
; otherwise, while creating the service, you will get a message that there are no serviceable components. See Step 3 for the code.
Step 6: Sign your assembly with a strong name key file. You can either select a new one on the project properties screen - Signing tab, or generate a strong name key using sn.exe.
Step 7: Compile the code, and hope you got it all right, and you will see the component in Component Services. Oh oh… there is a tip to create a COM+ component after successful compilation.
On the Properties screen, click on Build Events. Enter "C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\regsvcs /u $(TargetFileName)" for Pre-build event, and "C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\regsvcs $(TargetFileName)" for Post-build event. As you know or guessed, /u is to un-register the component, and pre-build event un-registers the component, and the post-build registers the component. The whole path is required to run regsvcs – a bug.
For the first time, the unregistered will fail as the component doesn't exist, so comment the pre-build event by keying rem in the front. You can also run these commands from the VS2005 command prompt. I could put both commands in post build, but noticed that the compile fails as the component is being used in other processes.
After registration, open Component Services to verify that your component does exist in the list of COM+ applications.
You can open Component Services using Start – Settings – Control Panel – Administrative Tools – Component Services.
The + sign on the component will be stationary, which indicates that the component is there not running, and there is no need to start as it will start automatically when it is called for the first time, or you can start by right click and Start. You can right click on the COM+ component and click Properties to set properties like user ID for running the component, pool size, security, queuing etc.
I usually set pooling based on the performance benchmarking, and you can play with it on the server to compare performance. You can also make it a service, and it will show in Services along the other services on your machine. Again, not going in to details as you can read about those in many books and articles.
Step 8: Last but not the least, call the COM+ component from an application.
Create an ASP page and the code to create an object of the COM+ component, using:
dim oBus
set oBus = server.CreateObject("busLayer.JayClass")
Once you create the object, call the method you want, like…
rtnVal = oBus.MyLog("asp msg")
This part is mundane, and I hope everybody understands old VBScript.
I am using the log4net .NET component to log the messages; for details of the log4net open source application, go here which will redirect to the new owner Apache.
There is no need to either copy your assembly to the GAC or copy any DLL to the system32 folder.
All the log4net code is out of the scope of this article, and I'll write about it at some other time. The same applies for using VB6 components in .ENT applications, and I have called VB6/C++ components from .NET and think it is easier (just add a reference to the unmanaged component, or run tlbimp) than using a .NET component in VB6/VBScript, as the Registry issues will not raise their heads.
Your comments are appreciated, which can save some hours of my life fighting with the code, and will also help the rest of us.
In part II, I'll explain the debugging of the .NET COM+ component.
Points of Interest
Note on method overloading: method overloading will not work with COM+ as the name is automatically appended by a suffix, and when you try to call the method, you will get an error message indicating that the parameters or parameter types are incorrect. Another hard to figure out issue, and took a few hours of my life (;-|).
Like to debug your application with a web or test project? Read Part II.