Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A COM class wizard for VS.NET 2003

0.00/5 (No votes)
15 Aug 2004 1  
A wizard to bring easy COM class creation to C#.

Sample Image - CSCom.jpg

Introduction

A few days ago, an article by Muhammad Musa Ali on Exposing COM interfaces of a .NET class library for Late Binding, appeared on Code Project to show all you VB guys how to create a class that can be used for interop with the world outside, i.e., inside a non-.NET client such as VB, VBA, JScript, ...

Being more of a C# guy myself, I was astounded that there is such a thing as a COM class wizard for VB.NET class libraries but not for C#. Something had to be done to correct this injustice ;-), and here it is: a COM class wizard for C# class libraries.

How to expose a class for COM interop (the C# way)

Muhammad depicts two ways to expose a class for COM interop: the easy way and the hard way.

The easy way is what VS.NET does when you let it create a COM class using the wizard. Basically, three GUIDs are created: for the class itself, the COM-visible interface this class implements, and for the events the class fires. These three GUIDs are stuffed into a ComClassAttribute and that's all about it. The compiler takes care of everything else.

Until that day, I had heard nothing about ComClassAttribute, and so I looked up the attribute on MSDN. Very quickly, I found out why this attribute had missed me completely: it's located in the Microsoft.VisualBasic namespace which I usually don't touch.

So, using this attribute in your C# classes might be possible if you add a reference to VisualBasic to your projects (admittedly, I didn't try it extensively), but this additional reference just to get a single class attribute seemed a bit overpowered.

So, I tried the hard way: telling the compiler what to do by defining all the interfaces and attributes myself.

There's a pretty exhaustive article on this topic on Code Project as well (Understanding Classic COM interoperability With .NET Applications), so I just depict the important steps:

Define an interface you want to expose to COM

COM is interface based, so your class has to tell COM what methods and properties it is going to expose.

Create your class and let it implement this interface

Somebody has to do the actual work, so implement the interface and fill your methods and properties with life.

Define an interface for events you want to raise

In case you want to raise events for COM clients, these events (or rather event handlers) have to be declared in the form of an interface as well. Luckily, you won't have to dig very deep into COM's event model or handle ConnectionPoint details, the framework does this for you.

Plug everything together using class attributes

This is where the internal plumbing happens. Tell the framework that your class exposes an interface for COM by assigning GUIDs, using ClassInterfaceAttribute on your class and InterfaceTypeAttribute on your interface. If you want events, then assign an InterfaceTypeAttribute to your events interface as well, and tell COM that this interface is to be used by specifying a ComSourceInterfacesAttribute. This attribute has several constructors with string arguments, but using these is also quite error-prone since .NET is very picky on the format of these types, so whenever possible, you should use the constructor receiving a Type like in:

[ComSourceInterfaces(typeof(IComClassEvents))]

Another important thing to do is to add a DispIdAttribute to each of your published methods, properties, and event handlers, or else they won't work as expected.

I'm inherently lazy, can't someone do this for me?

These steps shown above are quite error-prone if you have to do them by hand every time. VS.NET has the concept of wizards to create code, so why not use it?

To counter the VB.NET advantage of the COM class wizard, I dug a little into how these wizards work and how you could add your own wizard. Once you found the right files to change, this was quite easy.

Without going into too much detail, most of the wizards creating a class for you use a template file containing most of the code the final class will contain. This template then is copied and modified by a JavaScript script to insert the correct class or namespace names, for example. Additionally, project options can be modified as well (I used this to set the "Register for COM interop" flag to true in a class library receiving such a new COM class).

For example, the template for my new C# COM class has a class declaration like this:

public class [!output CLASS_NAME] : [!output INTERFACE_NAME]

The parts in brackets are then replaced by variable values you create as part of the JavaScript file, resulting in a valid class declaration, provided you specify the right arguments:

public class ComClass1 : IComClass1

Can I have this, too?

To try it out yourself, just use the link to the installation package I've added at the top of this article. It's an MSI package that's searching for the path of your VS.NET 2003 installation, and then installs the new wizard.

After installation, you can use a new template COM-Class in a C# class library project.

DISCLAIMER: Although I've tested the package on my computers, I cannot make any guarantees that it will work on your machine or that it will not harm your computer in any way. If you install the package and your computer blows up afterwards, I cannot be held responsible!

The installation package should work on German and English installations of VS.NET 2003. If you have a different language installed, then you might have to create copies of the template files and script in the corresponding subdirectory for your language code.

The wizard files can be found at <VS.NET 2003 Root>\VC#\VC#Wizards\CSharpComClass with subdirectories 1031 (German) or 1033 (English) in the Scripts and Templates subdirectories, resp.

I've also included the class template, JavaScript file, and wizard definition files (*.vsz and *.vsdir) as "source" for this article (link at the top), so if you don't trust my installer (or in case it doesn't work for you), you can use these files to add the new template by yourself.

Conclusion

If you follow the steps, then COM interop isn't really that hard. Just keep in mind that the environment you test your classes in also has its influence on the results. I spent almost a whole weekend wondering why the event fired by my test class (several seconds after one of my class' functions was called) was received in a scripting environment but (seemingly) not in a VBA client, although everything else worked. Finally, I found out that the object I wanted to receive the event had been destroyed before the event was raised :(.

And don't forget to vote if you like this article and the new wizard!

Version history

  • 13.07.2004 - Initial release.
  • 14.08.2004 - Update to 1.0.1.
    • Changed interface type for published class interface to ComInterfaceType.InterfaceIsDual to allow for early binding as well.
    • Changed suggested DispIds from 0 to 1.
    • Minor formatting changes in class template file.

Happy coding,

mav

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here