How to Make a c# Library Accessible to COM (VB6) – PART ONE

Live refused to accept my entry as a whole, forcing me to break it into two parts. See How to Make a c# Library Accessible to COM (VB6) – PART TWO for the second part.

Overview

This article explains how to write a Windows Library in c# that can be accessed from a VB6 application, which is surprisingly cryptic. Frankly, I think Microsoft could have included a “publish to COM” wizard, allowing us to select the desired members to export and it would instrument the code accordingly. If I did enough of this, I’d consider writing an add-on.

After struggling with this topic a few years back, I documented the process in Word. A recent question on Experts Exchange prompted me to port it to my blog. I fixed up the formatting a bit. Hopefully this will be helpful.

In general, exporting members to COM is accomplished by telling the compiler to register the Library through Interop at build time, creating and implementing an Interface for the methods and properties to be exposed and then decorating the Interface and Class with COM registration instructions. You will then be able to make a reference to the Library from VB6 in the same way you’d reference any other COM Library.

I won’t go into detail as to the underlying reason for some of the required steps. Frankly I don’t remember all the details, as I did this research quite some time ago. In fact, I’m writing this so that I’ll have it documented because each time I’ve needed to do this, I’ve had to dig through old code. I am, however, quite confident that these are the exact steps required. Leaving out any seemingly minor detail will prevent your Library from being exposed, which will be obvious when you try to access the Library or its members through VB.

Finally, there are many advanced options and techniques available to you when exposing managed code to COM that aren’t covered here.



Example Library

Figure 1 shows a very simple Library that we’re going to convert in this exercise. I’ve left otherwise required constructs out of the example, so as not to add noise that isn’t specifically pertinent to this exercise. For example, there are no standard #using statements.

In this example, we have an Automobile class with a public property, a public method, a private method, a hidden default constructor and a public custom constructor. I show the constructor deal because I often want to enforce the use of a custom by hiding the default. The relevance to this will soon be clear.

using System.Runtime.InteropServices; 
 
public class Automobile
{
    string _color;
 
    private automobile
    {
        // do nothing - simply hiding default constructor
        // to force instantiation through a custom constructor.
    }
 
    public automobile(string color)
    {
        _color = color;
    }
 
    public string color
    {
        get { return _color; }
        set { _color = value; }
    }
 
    public void StartEngine()
    {
    }
 
    private void TurnOnFan()
    {
    }
 
}

Figure 1 – Example Library

Step by Step Fun

  1. Register for COM Interop. This checkbox is in the Project’s Build Properties tab (Figure 2). Checking this causes the compiler to extract the COM interface information from the compiled .NET assembly and add them to the Registry. It’s similar to registering a COM Library using RegSvr32.exe, but for the .NET world. In fact, it actually uses the .NET “equivalent”, RegAsm.exe. In fact, this checkbox is really just a shortcut for the developer because you will need to manually register the Library with RegAsm.exe when you deploy the file to another machine. See the notes on this in Conclusion.

Also, the “Make Assembly COM-Visible” setting (shown below) is located in the project’s Properties, on the main Application Tab, behind the “Assembly Information” button. Enabling this checkbox will expose each public interface and class to COM. If you do not use this option, you must explicitly enable the option on a per item basis, via the “ComVisible” attribute. I’ve shown this attribute being used in Figure 5, but using the project-level setting means you won’t need to use this attribute.

A final note on ComVisible: You can explicitly hide members from COM by using ComVisible(false).

UPDATE: I recently discovered a situation were the global “Make Assembly COM-Visible” setting was enabled, but Types were NOT exposed (properly) to COM. I don’t yet know the full story. The Types were visible in the Object Browser in VB6, but not through Intellisense. Disabling this setting instantly let the explicitly marked up Classes appear in Intellisense.

clip_image002

Figure 2 – Register for COM Interop

clip_image004

Project-wide COM Visible

  1. Create an Interface. I forget why this is required, but it’s a VB or COM architectural requirement. Each method and property that you wish to export must be defined in an Interface. In our example, we want to expose the public attributes. Figure 3 contains the Interface.
public interface IAutomobile
{
    public string Color;
    public void StartEngine();
}

Figure 4 – Create an Interface

  1. Implement the Interface in your Class. This is obvious and shown in Figure 4.
public class Automobile : IAutomobile
{
    string _color;
 
    private automobile
    {
        // do nothing - simply hiding default constructor
        // to force instantiation through a custom constructor.
    }
 
    public automobile(string color)
    {
        _color = color;
    }
 
    public string color
    {
        get { return _color; }
        set { _color = value; }
    }
 
    public void StartEngine()
    {
    }
 
    private void TurnOnFan()
    {
    }
 
}

Figure 5 – Implement the Interface (Class)

  1. Decorate the Interface. We need to expose the Interface to COM. We’ll need a new GUID. You can generate one through Visual Studio’s Tool Menu, or any other way you want, but make sure it’s unique.

From what I’ve read, assigning a GUID manually is optional and some people don’t recommend it. The biggest argument against it is that you can make a mess if you’re not careful and reuse the same GUID, such as with copying and pasting code. I personally have used this technique and I’m not familiar with letting the compiler assign it, especially when it comes to rebuilding the DLL with existing bindings to its GUID.

[Guid("E9434C39-D939-45de-83C3-CE70C5D7FB36"),
ComVisible(true)]
public interface IAutomobile
{
    string Color { get; set; }
    void StartEngine();
}

Figure 5 – Decorate the Interface

  1. Decorate the Class. We need to expose the Class itself to COM. We’ll need another new GUID and set the Interface Type, as shown in Figure 6. The reason for specifying an interface type of “None” for the Class is to eliminate the automatic generation of an empty Interface. If you left this line out, you’d see no members. There’s logic behind this, which I won’t go into here, but it’s related to protecting you from breaking COM clients by changing the .net code after the initial release. This setting goes hand in hand with the manually built Interface we created in step 2.
[Guid("72BA12AE-B902-407c-8261-26BFCD7352BE"),
ClassInterface(ClassInterfaceType.None),
ComVisible(true)]
public class Automobile : IAutomobile
{
    string _color;
 
    private automobile
    {
        // do nothing - simply hiding default constructor
        // to force instantiation through a custom constructor.
    }
 
    public automobile(string color)
    {
        _color = color;
    }
 
    public string color
    {
        get { return _color; }
        set { _color = value; }
    }
 
    public void StartEngine()
    {
    }
 
    private void TurnOnFan()
    {
    }
 
}

Figure 6 – Decorate the Class

Technorati Tags:

This entry was posted in COM Interop. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s