By Robert M. Meagher
COM Interoperability is the feature of Microsoft .Net that allows managed
.Net code to interact with unmanaged code using Microsoft's Component
Object Model semantics. Most of the hoopla about COM Interoperability in
.NET centers on the ability of .Net code to invoke legacy COM objects (such
as ActiveX controls and COM objects encapsulated in DLLs).
But there exists the equally powerful and useful ability to develop new
components in .Net and make those components accessible to unmanaged code
as COM objects. That is the side of COM Interoperability that we discuss in
this article.
This article assumes that the reader has a basic familiarity with COM and
.Net terminology.
.Net Interfaces and Classes
The basis for accessing .Net objects either from other .Net code or from
unmanaged code is the Class. A .Net class represents the encapsulation of
the functionality (methods and properties) that the programmer wants to
expose to other code. In C++, classes supported multiple inheritance from
other classes. .Net doesn't support multiple inheritance, but does support
implementation of multiple interfaces. A .Net interface is the abstract
declaration of the methods and properties that classes which implement the
interface are expected to provide in their implementations. Declaring a
.Net interface doesn't generate any code, and a .Net interface is not
callable directly. But any class which implements ("inherits") the
interface must provide the code that implements each of the methods and
properties declared in the interface definition.
Example 1: C# Interface and Class Definitions
interface IDotNetInterface
{
void Initialize();
string Caption
{
set;
}
int ShowDialog(string sText);
}
class DotNetClass : IDotNetInterface
{
// Need a public default constructor for COM Interop.
public DotNetClass()
{
}
public void Initialize()
{
m_sCaption = "";
}
public string Caption
{
set { m_sCaption = value; }
}
public int ShowDialog(string sText)
{
System.Windows.Forms.MessageBox.Show(sText, m_sCaption);
}
private string m_sCaption;
}
Although a .Net class is not directly invokable from unmanaged code,
Microsoft has provided the capability of wrapping the .Net interface in an
unmanaged layer of code that exposes the methods and properties of the .Net
class as if the class were a COM object. There are two requirements for
making a .Net class visible to unmanaged code as a COM object:
Requirement 1: Creating a Description of the COM class and Interfaces
For a client of a COM object to have access to the object, the client needs
a description of the object -- how to locate it and how to call its methods
and properties. For a "real" unmanaged COM class, this description is
available in the form of a Type Library -- a binary description of the
GUIDs, classes, and interfaces (methods, properties, and parameters) that
the COM class supports.
.Net assemblies don't include information in Type Library compatible
format. So it is necessary for the programmer to run one of two
.Net-supplied utilities to extract the assembly description of a class into
a Type Library file. One utility is TLBEXP.EXE, the .Net Type Library
Exporter. This command line utility takes as input the name of an assembly
DLL file to be converted to a Type Library. The programmer can also specify
the name of a Type Library file to be created.
Example 2: Using TLBEXP to create a Type Library from an Assembly
tlbexp ComServer.dll /out:ComServer.tlb
Assembly exported to C:\Magellan\Source\Output\Debug\ComServer.tlb
Once a Type Library has been created, it can be referenced by a COM client
to obtain the information necessary for the COM client to bind to the
interfaces of the COM class, and activate the COM class at runtime.
Another command line utility for creating a Type Library from an assembly
is REGASM.EXE, the .Net Assembly Registration utility. In addition to
creating a Type Library, this utility also creates the Windows Registry
entries necessary for making the assembly visible as a COM object to
clients (see below).
Requirement 2: Registration of the COM Class and Interfaces
For a COM class to be accessible by the client at runtime, the COM
infrastructure must know how to locate the code that implements the COM
class. COM doesn't know about .Net classes, but .Net provides a general
"surrogate" DLL - mscoree.dll -- which acts as the wrapper and intermediary
between the COM client and the .Net class.
Example 3A: Registering an Assembly for COM Interop
regasm ComServer.dll
Example 3B: Registering an Assembly for COM Interop and generating a Type
Library
regasm ComServer.dll
Note that there is also a property in the Project Properties for a .Net
class library DLL called "Register for COM Interop". Setting this property
to True instructs the IDE to automatically register the assembly for COM
Interop each time you build it, so you don't have to perform this step
manually.
Accessing a .Net Class from Unmanaged Code
There are several ways to access a .Net class from unmanaged code.
Following is a C++ example. Note that this example uses the #import
directive to specify the exported .Net Type Library to use for extracting
description information for the .Net class. This example also uses the ATL
Smart Pointer template (CComPtr) to make creating and managing the lifetime
of a COM object easier and more automatic.
Example 4: C++ Client Code for Accessing DotNetClass
#include
#include
#include
#import "ComServer.tlb" raw_interfaces_only
void main()
{
CComPtr srpDotNet;
srpDotNet.CreateInstance(CLSID_DotNetClass, CLSCTX_ALL);
HRESULT hr = srpDotNet.Initialize();
if (SUCCEEDED(hr))
{
hr = srpDotNet.put_Caption(L"Client");
hr = srpDotNet.ShowDialog(L"Hello from the Client");
}
}
To access a .Net class from VB6 code, you would, in the VB6 IDE, do the
following:
- Select (References) menu. Browse to location of exported .Net Type
Library. Select that Type Library.
- Now write code that accesses the class. You should find that the
IntelliSense feature of VB6 can provide you hints of the methods and
properties available for any instance of the .Net class that you declare in
your code.
Summary
There is a lot more to COM Interoperability and taking advantage of
managed-unmanaged code interaction than was presented here, but this
article has attempted to make the programmer aware of the capability for
developing reusable .Net code and class libraries that can be invoked from
both managed and unmanaged code.