Welcome!

.NET Authors: Liz McMillan, Peter Silva, Yakov Werde, Matthew Pollicove , Corey Roth

Related Topics: .NET

.NET: Article

Do you COM? Dealing with Legacy Projects

".NET 3.0 ... high tech and still unexplored in its entirety"

To verify that the component has been correctly added to the Windows Registry, open the OleView tool. Expand the Grouped by Component Category node, then go to the .NET Category node. Now, assuming that the main namespace where the HelloClass type has been defined is called Hello, you will find your COM object here (search for Hello.HelloClass).

To perform the reverse operation, all that's needed is to add the /unregister (or /u) switch to the above command line.

If you're not happy with the default programmatic identifier generated for this class, there is an alternative: just use ProgIdAttribute on your class. Normally, the programmatic identifier that gets written in the registry is composed of the namespace where the class is defined and the name of the class. This might result in a not-too-easy to remember ProgID if the namespace chain is too long. ProgIdAttibute takes care of this, giving you control over this aspect too. Note that ProgIDs are limited to 39 characters, including punctuation (only dots are accepted.) To use this attribute, refer to the following snippet:

namespace This.Is.A.Very.Looong.Loooong.Namespace {
    [ProgId("My.Shorter.ProgID")]
    public class MyClass {
    }
}

Here, the default ProgID would have been This.Is.A.Very.Looong.Loooong.Namespace.MyClass. Now COM clients can consume this class using the My.Shorter.ProgID programmatic identifier.

After registration, the component can be used from any Win32 application. However, there's one more word to be added here. The recommended way of registering a .NET library as a COM component is to assign a strong name to your project, and then install the resulting DLL in the Global Assembly Cache (GAC). In fact, using the command line that I've just shown assumes that Hello.dll will eventually be added to the GAC.

If you really need to deploy the component in its own folder and not place it in the Global Assembly Cache, you have to add the /codebase switch to the command prompt when registering the assembly.

This will result in an extra CodeBase entry in the registry, pointing to the correct location of the resulting DLL. You can use this switch for debugging purposes as well.

Once the object has been correctly registered in the Windows Registry, you can use it from any COM client. For example, here's how you can consume the Hello.HelloClass component from Visual Basic 6:

Dim obj As Object
Set obj = CreateObject("Hello.HelloClass")
MsgBox obj.SayHelloTo("Jack")
Set obj = Nothing

Pretty cool, right? While we're at it, I can show how you can use the same .NET class from C++. Since there's a little bit more code involved in this case, I invite you to have a look at Listing 1 at the end of this article (to keep things simple, error checking was omitted from the code).

You might wonder what customizations can be made at register/unregister time, such as writing additional registry entries or the like. The news is good in this matter, too - .NET defines two wonderful method-level attributes: ComRegisterFunctionAttribute and its counterpart ComUnregisterFunctionAttribute. The methods on which they are used will be called whenever the assembly is registered or unregistered:

public class HelloClass {
    ...code omitted...
    [ComRegisterFunctionAttribute]
    public static void RegisterHelloClass(Type t) {
       MessageBox.Show("Registered type: " + t.Name);
    }
    [ComUnregisterFunctionAttribute]
    public static void UnregisterHelloClass(Type t) {
       MessageBox.Show("Unregistered type: " + t.Name);
    }
}

As shown above, the methods used with the two attributes must be static. You can apply both attributes to the same method, but I don't recommend it unless you have a good reason to do so. There can be more such methods in your code, one for each class that gets exposed to COM, but you can't have more than one registration (or deregistration) method for the same class.

I have to add here that all the functionality described so far is also accessible in two other ways. One is to check the "Register for COM Interop" option in the project properties. Note that the component will be registered as if you were using the /codebase switch in the Assembly Registration Tool.

The other method is to do it programmatically. The .NET Framework offers a special class for this in the System.Runtime.InteropServices namespace called RegistrationServices. This class contains a few useful methods that you might find interesting, such as the possibility to enumerate all registerable types from an assembly.

Isn't it nice when you have such great flexibility at hand?

Still, there's a catch in all that I described so far. Things work as expected, that's for sure, but have you ever wondered why it works at all? The question seems silly at first; in fact, this is exactly what those attributes are meant to do, right?

Well, yes, but there is also a hidden face you didn't see. Each exposed type will automatically get a few other attributes applied to them. It might help a project if you know a little bit more about this and will eventually lead to better designs. It's time to discuss the more serious stuff. It's time to talk about...

Interfaces, Interfaces Everywhere...
COM is all about implementing well-known or custom interfaces, all of them ultimately deriving from the ubiquitously IUnknown interface with its three methods: QueryInterface, AddRef, and Release. Every .NET type that will eventually be exposed to COM must emulate this somehow, otherwise the .NET component won't be of any use for COM clients.

This is accomplished via ClassInterfaceAttribute, an attribute defined in the System.Runtime.InteropServices namespace that can be applied to your classes. When used, ClassInterfaceAttribute accepts a ClassInterfaceType argument that will establish the type of interface implemented by the class. You can have a dual class interface (ClassInterfaceType.AutoDual), a dispatch-only interface (ClassInterfaceType.AutoDispatch), or a class for which you'll implement your own interface (ClassInterfaceType.None).


More Stories By Catalin Sandu

Catalin Sandu is a software developer at RomSoft (www.rms.ro) and has 10 years of experience. He is both a Microsoft Certified Professional (on C++ and .NET), and an Advanced ColdFusion MX 7 Developer. Catalin is also a member of the British Computer Society since 2005.

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.