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

Integrating with COM Components In C#

0.00/5 (No votes)
11 Dec 2011 1  
How CLR provides support both for exposing C# objects as COM objects and for using COM objects from C#.

Introduction

The CLR provides support both for exposing C# objects as COM objects and for using COM objects from C#. Additionally, CLR components can make use of COM+ services and can be used as configured components by CLR and classic COM applications.

Binding COM and C# Objects

Interoperating between COM and C# works through either early or late binding. Early binding allows you to program with types known at compile time, while late binding forces you to program with types via dynamic discovery, using reflection on the C# side and IDispatch on the COM side.

When calling COM programs from C#, early binding works by providing metadata in the form of an assembly for the COM object and its interfaces. TlbImp.exe takes a COM type library and generates the equivalent metadata in an assembly. With the generated assembly, it's possible to instantiate and call methods on a COM object just as you would on any other C# object.

When calling C# programs from COM, early binding works via a type library. Both TlbExp.exe and RegAsm.exe allow you to generate a COM type library from your assembly. You can then use this type library with tools that support early binding via type libraries such as Visual Basic 6.

Exposing COM Objects to C#

When you instantiate a COM object, you are actually working with a proxy known as the Runtime Callable Wrapper (RCW). The RCW is responsible for managing the lifetime requirements of the COM object and translating the methods called on it into the appropriate calls on the COM object. When the garbage collector finalizes the RCW, it releases all references to the object it was holding. For situations in which you need to release the COM object without waiting for the garbage collector to finalize the RCW, you can use the static ReleaseComObject method of the System.Runtime.InteropServices.Marshal type.

The following example demonstrates how to change your MSN Instant Messenger friendly name using C# via COM Interop:

// RenameMe.cs - compile with:
// csc RenameMe.cs /r:Messenger.dll
// Run RenameMe.exe "new name" to change your name
// as it is displayed to other users.
// Run TlbImp.exe "C:\Program Files\Messenger\msmsgs.exe"
// to create Messenger.dll
using System;
using Messenger;
class MSNFun {
static void Main(string[ ] args) {
MsgrObject mo = new MsgrObject( );
IMsgrService ims = mo.Services.PrimaryService;
ims.FriendlyName = args[0];
}
}

You can also work with COM objects using the reflection API. This is more cumbersome than using TlbImp.exe, but is handy in cases in which it's impossible or inconvenient to run TlbImp.exe. To use COM through reflection, you have to get a Type from Type.GetTypeFromProgID() for each COM type you want to work with. Then, use Activator.CreateInstance() to create an instance of the type. To invoke methods or set or get properties, use the reflection API:

using System;
using System.Reflection;
public class ComReflect {
public static void Main( ) {
object obj_msword; // Microsoft Word Application
Type wa = Type.GetTypeFromProgID("Word.Application", true);
// Create an instance of Microsoft Word
obj_msword = Activator.CreateInstance(wa);

// Use the reflection API from here on in...
}
}

Exposing C# Objects to COM

Just as an RCW proxy wraps a COM object when you access it from C#, code that accesses a C# object as a COM object must do so through a proxy as well. When your C# object is marshaled out to COM, the runtime creates a COM Callable Wrapper (CCW). The CCW follows the same lifetime rules as other COM objects, and as long as it is alive, a CCW maintains a traceable reference to the object it wraps. This keeps the object alive when the garbage collector is run.

The following example shows how you can export both a class and an interface from C# and control the Global Unique Identifiers (GUIDs) and Dispatch IDs (DISPIDs) assigned. After compiling IRunInfo and StackSnapshot, you can register both using RegAsm.exe.

// IRunInfo.cs
// Compile with:
// csc /t:library IRunInfo.cs
using System;
using System.Runtime.InteropServices;
[GuidAttribute("aa6b10a2-dc4f-4a24-ae5e-90362c2142c1")]
public interface IRunInfo {
[DispId(1)]
string GetRunInfo( );
}

// StackSnapshot.cs
// compile with csc /t:library /r:IRunInfo.dll StackSnapShot.cs
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
[GuidAttribute("b72ccf55-88cc-4657-8577-72bd0ff767bc")]
public class StackSnapshot : IRunInfo {
public StackSnapshot( ) {
st = new StackTrace( );
}
[DispId(1)]
public string GetRunInfo( ) {
return st.ToString( );
}
private StackTrace st;
}

Common COM Interop Support Attributes

The FCL provides a set of attributes you can use to mark up your objects with information needed by the CLR interop services to expose managed types to the unmanaged world as COM objects.

COM Mapping in C#

When you use a COM object from C#, the RCW makes a COM method look like a normal C# instance method. In COM, methods normally return an HRESULT to indicate success or failure and use an out parameter to return a value. In C#, however, methods normally return their result values and use exceptions to report errors. The RCW handles this by checking the HRESULT returned from the call to a COM method and throwing a C# exception when it finds a failure result. With a success result, the RCW returns the parameter marked as the return value in the COM method signature.

COM+ Support

The CLR also includes special plumbing and interop services that allow CLR classes to be deployed as COM+ configured components. This allows both CLR clients and classic COM clients to make use of COM+ services for building scalable applications.

What Is COM+?

COM+ provides a set of services that are designed to help build scalable distributed systems, such as distributed transaction support, object pooling, Just-In-Time activation, synchronization, role-based security, loosely coupled events, and others.

These services are provided by a runtime environment called the COM+ runtime, and are based on the idea of intercepting new COM object creation and (possibly) method calls to layer in the additional services as needed.

COM classes that use COM+ services are called Configured Components because the exact set of services each COM class requires is controlled and configured using declarative attributes that are stored in a metadata repository called the COM+ Catalog.

The COM+ Catalog groups a set of configured components together into something called an Application, which also has metadata settings that control which process the COM components end up in when they are created at runtime (the options here are Library and Server, where Library components end up in the creator's process, while Server components are hosted in a separate process), the security principal the new process runs as, and other settings.

Using COM+ Services with CLR Classes

Naturally, CLR classes can also take advantage of COM+ services. Although the underlying implementation of this is currently the classic COM+ runtime, the mechanics of it are largely hidden from the .NET programmer, who may choose to work almost entirely in terms of normal CLR base classes, interfaces, and custom attributes.

The bulk of the functionality in .NET for using COM+ services is exposed via the System.EnterpriseServices namespace. The most important type in this namespace is the ServicedComponent class, which is the base class that all CLR classes must derive from if they wish to use COM+ services (i.e., if they want to be configured components).

There is a suite of custom attributes in this namespace that can control almost all of the configuration settings that would otherwise be stored in the COM+ Catalog. Examples of these attributes include both assembly-level attributes which control the settings for the entire COM+ application, the ApplicationActivationAttributes, which controls whether the CLR class is deployed in a COM+ Library or Server application, and component-level attributes, which declare and configure the COM+ services the CLR class wishes to be provided at runtime. Examples of component-level custom attributes include the TransactionAttribute (which specifies the COM+ transaction semantics for the class), the JustInTimeActivationAttribute (which specifies that the CLR class should have JITA semantics), the SynchronizationAttribute (which controls the synchronization behavior of methods), the ObjectPoolingAttribute (which controls whether the CLR class is pooled), and many, many others.

Although ServicedComponent serves as a special base class which signals the .NET Framework that a class needs COM+ services, it also provides other capabilities. In classic COM+ work, COM classes implement interfaces such as IObjectConstruct and IObjectControl to customize aspects of their behavior. When using COM+ services in .NET, your classes can override virtual methods provided by ServicedComponent that mirror the functionality in IObjectConstruct and IObjectControl, allowing a very natural, .NET-centric way of accomplishing the same thing.

Other important classes in the System.EnterpriseServices namespace include ContextUtil and SecurityCallContext. These classes provide static methods that allow a CLR-configured component to access COM+ context. This is used to control things like transaction status and to access information such as the security role a caller is in.

Lastly, let's discuss deployment. Deploying traditional COM+ applications requires one to configure the component's COM+ Catalog settings. This is typically done using either the COM+ Explorer (by hand, really only suitable for toy applications) or using custom registration code. When configuring CLR classes, there are two different approaches.

The first approach is using the RegSvcs.exe command-line tool. This tool performs all the relevant COM Interop and COM+ Catalog configuration, using both command-line options and the custom attributes applied to your assembly and classes to control the COM+ metadata. While this requires an extra step, arguably this approach is the most powerful and flexible, resulting in CLR-configured classes that can be used from both COM and .NET clients.

Alternatively, the .NET COM+ integration is able to automatically register classes that derive from ServicedComponent in the COM+ catalog when they are first instantiated. This has the advantage of not requiring any additional setup, but also has several disadvantages, most notably that the client code that indirectly causes the registration to occur needs elevated privileges, and until the class is configured, it is invisible to COM clients.

A simple C# configured class might look like this:

using System;
using System.EnterpriseServices;

[assembly:ApplicationName("MyCOMPlusApplication")]
[assembly:ApplicationActivation(ActivationOption.Server)]

[ObjectPooling(true), Transaction(TransactionOption.Required)]
public class MyConfiguredComponent : ServicedComponent {
public void DoDBWork( ) {
ContextUtil.SetAbort( );
// ... do database work...
ContextUtil.SetComplete( );
}
public override bool CanBePooled( ) { 
return true;
}
}

For more information on Integrating with COM Components in C#, you can send an email to vijayksh111@gmail.com.

History

  • 11th December, 2011: Initial version

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