How I came to love COM interoperability

Well, maybe the title of this post is slightly exaggerated, but this is the story about how I – despite strong reluctance to do so – successfully managed to expose a .NET library of mine to COM. Furthermore, during this process, my original .NET library became even better and I actually ended up kind of liking the additional COM API.

Obviously, as most other .NET developers, I have had to deal with the tremendous amount of unmanaged code out there. Even if I would rather avoid it, from time to time, I have had to use the .NET interoperability services to consume some ActiveX component or other type of unmanaged legacy code. And I have learned to live with it. But why would someone ever think of exposing a nice and clean .NET library for a development platform (COM) that was deprecated decades ago?

Anyway, recently I found myself in a situation where I was left no choice. I had made this terrific .NET library with loads of nice functionality when the client required that the same functionality was made available through COM.

There are lots of explanations out there on the Internet on how to expose .NET assemblies to COM – for example this article on CodeProject. The reason why I bother to write this post anyway is that none of the resources that I have found on the Internet describes the exact approach that I eventually chose for my project. Also, I did not find any resources giving an overview of all the small challenges I met in the process. And as we all know, the devil is in the detail.

The Example Code

The example code for this post is based on the legendary “Hello World!” example. Compared to the normally very simple structure of such examples, my code might seem unnecessarily complicated to overcome the simple task of displaying the famous text message, but still it is relatively simple and elegantly illustrates all of the challenges that I met when working with the real code base.

The central class in the example library is a Greeter class that has a dependency to an IMessageWriter instance that is injected into the Greeter class through the constructor (yes, this is dependency injection in action):

The IMessageWriter instance is used in the Greet method to write the message. The GreetingType decides the exact phrasing of the greeting (much more about this later). The IMessageWriter interface contains a single Write method:

The example library comes with a single concrete implementation of the IMessageWriter interface – a ConsoleMessageWriter that writes a text message to the console:

From a console application the following code creates a silly greeting:

The Overall Approach

Now, let’s dig into the matter. Probably because of my initial reluctance to deal with the COM interoperability at all, I decided to make a clear rule for myself – a self-imposed dogma so to speak. I would under no circumstances “pollute” my original .NET library with any COM-related stuff such as COM-specific interfaces or any of the ComVisible-, Guid– or ClassInterface attributes. I would allow no references to the System.Runtime.InteropServices namespace whatsoever. Also, I would not accept major degradations of my original library. So I ended up with a project structure like this:

dependencies

All the COM-specific stuff is encapsulated in the ClassLibrary.Interop assembly while my original ClassLibrary assembly remains a clean .NET library.

In the ClassLibrary.Interop assembly I explicitly define all the COM interfaces and decorate them with the Guid attribute:

Furthermore I create new classes inheriting from the original ones and implementing the corresponding explicitly defined COM interface. I decorate the classes with the Guid attribute and the ClassInterface attribute with the ClassInterfaceType.None parameter. The ClassInterfaceType.None parameter prevents the class interface from being automatically generated when the class metadata is exported to a COM type library. So in the below example, only the members of the IGreeter interface will be exposed:

I don’t bother decorating the individual classes with the ComVisible attribute because the whole point is that in the ClassLibrary.Interop assembly I only deal with .NET types that I want to expose for COM, so instead I declare this once and for all in the AssemblyInfo file:

Dealing with the Challenges

As mentioned earlier, I met a few challenges on the way – mostly because of .NET/C# features that are not supported in COM. In the following, I will describe the individual challenges and the solutions to them.

Constructors with parameters

COM does not support constructors with parameters. COM requires default (parameterless) constructors.

As shown earlier, the Greeter class uses dependency injection and requires an instance of an IMessageWriter interface provided through its constructor:

So what I did was that I created an additional protected parameterless default constructor and a protected MessageWriter property. The fact that these two additional members are protected is an important point because then I can use them from my Greeter extension class in the ClassLibrary.Interop assembly to provide COM interoperability while still hiding these members from “normal” use of the Greeter class within the .NET Framework – thus forcing the consumer to use the public constructor:

Then I can introduce an Initialize method in the COM interface of the Greeter class and use this method to set the MessageWriter property.

So now, from a COM consumer I will have to first create the Greeter object using the default constructor and then call the Initialize method.

Overloaded methods

Overloaded methods are not supported in COM. In the Greeter class I do have two Greet methods with different signatures – one always making a neutral greeting and one where I can provide a specific greeting type as a parameter:

The only way to deal with this problem is to introduce different names in the COM interface:

Generics

.NET generics is gibberish for COM. So if you have made any generic classes or methods or if you use any of the built-in generic types then you have to be a bit creative. In the Greeter class I am using the generic ReadOnlyCollection<> to keep the greeting history:

The solution to this problem is pretty straight forward. Simply let the Greeter extension in the ClassLibrary.Interop assembly return an arrays of strings instead:

Inheritance

One challenge that I met is in a different category than the others. This challenge was not due to the missing COM support for certain .NET/C# features. Rather, it was due to my self-imposed dogma about keeping my original .NET library free of COM-related stuff. As I wanted to extent the original .NET types with COM interoperability using inheritance, only inheritable types could be extended. .NET types like struct and enum are not inheritable.

So I had to change a couple of structs to classes in my original library, which didn’t really bother me too much.

The enums, however, were a bit trickier. What I did was to introduce my own Enumeration class instead of using enums. This was one of the changes that I actually consider a major improvement to my original code. I have always found it annoying that enums could not be extended with for example a display name (for example including spaces). By introducing an Enumeration class, exactly this can be done:

The whole discussion about using enumeration classes instead of enums is worth a post by itself, but another advantage worth mentioning is that this approach can reduce the number of switch statements that inevitably follows from the usage of enums. Look how elegantly the greeting text, in the form of the Greeting property, has become a detail of a greeting type:

Now the individual greeting types can be defined, e.g. a neutral greeting type:

Or a silly greeting type:

Static methods

The GreetingType enumeration class brings us to the last of the challenges. In the GreetingType enumeration class I define 3 static methods – one for each of the greeting types.

But unfortunately static methods are not supported in COM. So, for the COM interface I have to expose the 3 greeting type classes instead – here illustrated by the GreetingTypeCasual class:

This is why I had to make the original greeting types public. If I wasn’t going to expose my assembly to COM, I would have made the GreetingTypeNeutral (and the other greeting types) internal – or even private classes within the GreetingType class.

COM Registration

When all challenges are overcome and the ClassLibrary.Interop assembly is ready, it must be properly registered.

In my ClassLibrary.Interop project I have checked the “Register for Com interop” option under the projects Build properties. This will do the trick on your own machine.

If you want to deploy the COM version of the library to other machines, you have to use the assembly registration tool RegAsm. If you call it from a Windows batch file placed in the same folder as the assembly itself, you can for example use the following syntax:

This approach requires that the assembly is signed with a strong name (even if not put in the GAC).

My guess is that most COM consumers run in 32 bit. If you want to register for 64 bit consumers, you should call the 64 bit version of RegAsm found in c:\Windows\Microsoft.NET\Framework64.

VBA Sample

And finally, here is some sample code using the COM API from a Visual Basic for Applications (VBA) macro:

Summary

This post describes an approach for exposing a .NET assembly to COM by handling all the COM-specifics in a dedicated ClassLibrary.Interop assembly without having to compromise the original ClassLibrary assembly.

Exposing .NET assembly functionality to COM does not necessarily need to be a hassle. Yes, there are indeed some challenges to overcome and, for sure, my personal preference will always be to use the .NET assembly directly. However, I do see some advantages in providing a dedicated COM API acting as a sort of “higher level” scripting API for other than hardcore .NET programmers. I kind of like the way that the explicitly defined COM interfaces in the ClassLibrary.Interop assembly acts as a facade to the full functionality, and how for example abstract base classes and interfaces are hidden to the COM API user.

The source code can be downloaded from my CodeProject article .

Leave a Reply

Your email address will not be published. Required fields are marked *