A former colleague emailed me this week to ask about registering C# assemblies for COM Interop. He was convinced that you needed to do something special to advertise a COM object from C#, since we’d used the ComClass
attribute in VB.NET. Also, Rockford Lhotka wrote (in July 2004):
“There are several COM interop features in VB that require much more work in C#. VB has the ComClass attribute and the CreateObject method for instance.”
I was able to assure him that for his scenario – late-bound use of an object – all you need to do is use regasm
to register the assembly. That registers every public class in the assembly, except any that are marked [ComVisible(false)]
. (A quick aside here – the registration time is longer the more public classes you have, so you should only make the classes you actually want to expose to COM public.)
But if regasm
registers all public classes in an assembly, what is the ComClass
attribute for? The documentation suggests it’s required but as we’ve just seen it isn’t, at least not for this scenario.
It’s a signal to the VB.NET compiler to do three things:
- Emit an interface definition automatically generated from the methods and properties in the class – a so-called Class Interface.
- Add this interface to the
Implements
list for the class (so the class appears to implement this interface). - Emit a
<ClassInterface(ClassInterfaceType.None)>
attribute to suppress auto-generation of a class interface.
That last one is interesting. The .NET Framework supports automatic generation of class interfaces at registration time using the ClassInterfaceAttribute
attribute. That generated interface is either a dispatch-only or a dual interface depending on the type selected. So what’s the difference?
Let’s take a simple test:
Imports System.Runtime.InteropServices
<ComClass()> _
Public Class WithComClassAttribute
Sub Test()
End Sub
End Class
Public Class WithNoAttribute
Sub Test()
End Sub
End Class
<ClassInterface(ClassInterfaceType.AutoDual)> _
Public Class WithClassInterfaceAttribute
Sub Test()
End Sub
End Class
First, let’s look at the generated code with the help of Reflector (in C# mode):
namespace VbComTlbTest { [ClassInterface(2)] public class WithClassInterfaceAttribute { // Methods public WithClassInterfaceAttribute(); public void Test(); }
[ClassInterface(0), ComClass] public class WithComClassAttribute : _WithComClassAttribute { // Methods public WithComClassAttribute(); public void Test();
// Nested Types public interface _WithComClassAttribute { // Methods [DispId(1)] void Test(); } }
public class WithNoAttribute { // Methods public WithNoAttribute(); public void Test(); } }
Notice that VB has generated the automatic class interface as a nested type, inside the class it belongs to.
To see the difference from a COM perspective, we need to look at the type library. Export it using tlbexp
, then use OLE View from the Platform SDK (also supplied with VS.NET) to load the type library (File/View TypeLib).
I’m not going to post the entire type library IDL shown by OLE View, but note that not setting an attribute generates an empty class interface definition (equivalent to setting ClassInterfaceType.AutoDispatch
), and that setting AutoDual
generates a class interface containing all inherited methods, including ToString
, Equals
, GetHashCode
and GetType
from System.Object
.
So to duplicate VB.NET’s behaviour with the ComClass
attribute in C# – if you need to, bearing in mind that this really only applies to early-binding scenarios – you need to define an interface then implement that interface, and use ClassInterfaceType.None
. This (declaring interfaces) is, of course, what you have to do in traditional C++.
2 comments:
What a supprise :P was searching for COMClass, and found a blog of somone who went to aston and used to live in ABG :P
Ok, perhaps not such a big supprise ;p
Thanks for this explanation Mike. It was exactly the information I was after!
Post a Comment