A Better Way To Call A .NET Assembly From COM

In my previous post Call a C# Assembly From VB6 I mentioned that there was a better way to pull off the interop required for using a .NET assembly in VB6. You can think of the technique used in that article as the “quick and dirty” approach. It works, but leans heavily on .NET’s ability to automatically specify the settings in the generated COM interface. That’s ok, but there is a cost: you have to recompile your COM clients every time you recompile your .NET component, and you cannot use late binding which, for example, is required for use by scripting clients. For just a little extra effort, you can exercise a greater level of control, and not have to pay these costs.

The technical background for what follows is covered very nicely in many places. I’m just going to provide a “how to” in what follows. For background on why it is done this way, you can check out COM Interop Exposed, or another similar resource on the web.

I’ll reengineer the Reverser class from the Call a C# Assembly From VB6 article to use this better approach.

Create an Interface

Create an interface for the methods you want to expose in COM. The interface for the Reverser class would be defined like so:

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Text;
   4: using System.Runtime.InteropServices;
   5:
   6: namespace String.Utilities {
   7:     [Guid("F60865B6-D2D7-4435-8B0B-D2A09D88548C")]
   8:     [ComVisible(true)]
   9:     public interface IReverser {
  10:
  11:         string Reverse(string str);
  12:
  13:         string BetterReverse(string str);
  14:     }
  15: }

Notice that we have added a GUID. We have done this to control versioning. In the prior article we noted that  you should recompile your COM clients every time the .NET component is changed. By specifying a GUID for our interface and classes, we will be able to change the implementation of our .NET component without requiring a recompile of our COM clients. See COM Interop Exposed for more details.

Implement the Interface

Our new Reverser class will implement the new IReverser interface:

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Text;
   4: using System.Runtime.InteropServices;
   5:
   6: namespace String.Utilities {
   7:
   8:     [Guid("80705AFF-708F-46f9-BA50-D69A96753A57")]
   9:     [ComVisible(true)]
  10:     [ClassInterface(ClassInterfaceType.None)]
  11:     public class Reverser : IReverser {
  12:
  13:     // Types must have a public default constructor   
  14:     // to be instantiated through COM. Managed public  
  15:     // types are visible to COM, but without a public  
  16:     // default constructor (a constructor without   
  17:     // arguments), COM clients cannot create an   
  18:     // instance of the type.  
  19:     public Reverser() {
  20:     }
  21:
  22:     #region IReverser Members
  23:
  24:     public string Reverse(string str) {
  25:         // This method is from Mladen Prajdić's   
  26:         // I want some Moore blog:  
  27:         // http://weblogs.sqlteam.com/mladenp/
  28:         // archive/2006/03/19/9350.aspx  
  29:
  30:         char[] charArray = str.ToCharArray();
  31:         int len = str.Length - 1;
  32:
  33:         for (int i = 0; i < len; i++, len--) {
  34:             charArray[i] ^= charArray[len];
  35:             charArray[len] ^= charArray[i];
  36:             charArray[i] ^= charArray[len];
  37:         }
  38:         return new string(charArray);
  39:     }
  40:
  41:     public string BetterReverse(string str) {
  42:         // This method is from Mladen Prajdić's   
  43:         // I want some Moore blog:  
  44:         // http://weblogs.sqlteam.com/mladenp/
  45:         // archive/2006/03/19/9350.aspx  
  46:
  47:         char[] charArray = new char[x.Length];
  48:         int len = x.Length - 1;
  49:         for (int i = 0; i <= len; i++)
  50:             charArray[i] = x[len - i];
  51:         return new string(charArray);
  52:     }
  53:
  54:     #endregion
  55:     }
  56: }

Notice that this class also defines a GUID. As noted for the IReverser interface, this is also done for the actual class to control versioning. Also, the ClassInterfaceAttribute specifies that no automatic interface is to be generated. In this case, the first interface implemented by the class will be the default interface in COM. This allows us to support late binding as well as early binding in COM clients (previously, only early binding was supported).

Notes

You will still have to set the project to register for COM interop. Note however that there is no need to change the ComVisiible setting in the AssemblyInfo.cs file:

   1: [assembly: ComVisible(false)]

In fact, by leaving it false, we can control exactly which interface methods are visible by overriding this setting with the [ComVisible(true)] attribute on an interface by interface, or method by method basis.

Leave a comment

Name: (Required)

eMail: (Required)

Website:

Comment: