The .NET platform is a major shift from the earlier COM technology, which dominated the Microsoft world for many years. Although new development may be carried out using .NET, there will be a need to reuse some of the functionality existing in the form of COM components. In this article, I examine some COM and .NET interoperability techniques.
COM to .NET: A shift in technology
Though a COM component and a .NET assembly share the same .dll extension, things are not the same internally. A .NET assembly does not need information about it to be stored in the registry, but instead holds the type information in the form of metadata. This metadata is present within the assembly itself. Further, a .NET assembly is not based on the IUnknown interface, which is an interface exposed by all COM objects.
The code that exists in the form of COM is known as unmanaged code, because the execution of this code is not managed by the common language runtime (CLR). Code written with the .NET Framework is managed by the CLR and is hence known as managed code.
It is impractical to rewrite every piece of code in .NET, because a lot of functionality exists in COM form. Moreover, more code exists in COM today than .NET, because .NET is a relatively new platform. From this fact arises the need for interoperating between COM and .NET. The .NET Framework has dedicated a namespace for this purpose. The System.Runtime.InteropServices namespace provides classes that can be used for accessing COM objects from .NET. We will begin by calling COM objects from .NET—a common necessity.
Calling COM objects from .NET
The code within a COM object is not executed by the CLR. The mechanism followed to expose a COM object to .NET is as follows: The COM object is encapsulated within a wrapper class, and this exposes the COM object as a .NET assembly to the CLR. This wrapper is known as the runtime callable wrapper (RCW). This process is illustrated in Figure A.
|Runtime callable wrapper (RCW)|
This wrapper class (RCW) acts as a bridge between the unmanaged and managed code, and all operations are routed through this class. That's enough theory; let’s take a quick look at how this can be done. The entire process of generating an RCW is automatically handled by Visual Studio.NET.
Interoperability using Visual Studio.NET
Let’s assume we need to utilize Microsoft Excel in our application. We can do so by accessing the Project menu and References and choosing Microsoft Excel Object Library from the COM tab, as shown in Figure B.
|Choosing the Microsoft Excel Object Library|
As soon as we do so, Visual Studio.NET automatically generates an RCW. The naming convention followed is Interop.COMObject.dll. In this case, the RCW is called Interop.Excel.dll, which is referenced in the .NET application; we can instantiate and use Excel objects in code. The same is true for any custom COM component authored by you. The only requirement is that the COM component must be registered.
You can also use ActiveX Controls from .NET. To do so, right-click on the Toolbox and select Customize Toolbox. From the COM Components tab, select (for example) Microsoft ListView Control and check the checkbox beside it (see Figure C). The Listview control will be added to the Toolbox.
|Selecting the Microsoft ListView Control|
As soon as you place the Listview control on the form, Visual Studio.NET generates the RCW for the ActiveX Control. The naming convention followed is AxInterop.ActiveXControl.dll, so in this case it is called AxInterop.MSComctlLib.dll.
Interoperability without Visual Studio.NET
Suppose you are not using Visual Studio.NET. Another way of achieving the same result is to use the .Net Framework tool called the Type Library Importer (tlbimp.exe). This tool can be used to convert type definitions in a COM library into a .NET assembly.
For example, if you have a COM DLL named MyCOMComponent.dll, you can build an RCW for it as follows:
Tlbimp MyCOMComponent.dll /out:MyRCW.dll
In the case of ActiveX Controls, you can use the .NET Framework tool called the ActiveX Importer to convert the type definitions in an ActiveX Control into a Windows Forms Control. It can be used from the command line as follows:
Wrap it up
If you are wondering how Visual Studio.NET or the command-line tools generate these wrappers, here is the key. The .NET Framework Class Library has a class called TypeLibConverter (System.Runtime.IneropServices.TypeLibConverter), which exposes a method called ConvertTypeLibToAssembly that can be used to write your own tool; this tool generates an RCW. The same is true for the ActiveX controls—the AxImporter Class (System.Windows.Forms.Design.AxImporter) can be used to generate RCWs for ActiveX controls.
One of the main architectural differences between .NET and COM is memory management. The CLR provides automatic memory management by means of a garbage collector that manages the allocation and release of memory. This is done periodically, so an object is not immediately destroyed when it goes out of scope or when it is set to Nothing (Visual Basic.NET, the same as null in C#).
When an RCW goes out of scope or is set to Nothing, the object is not destroyed immediately, and the corresponding COM object will also reside in memory—which might not be desirable. In such situations, steps should be taken to clean up the memory. You can do so by calling the garbage collector, by calling the System.GC.Collect method, or—the preferred way—by calling the Marshal.ReleaseComObject method on the RCW.
It is heartening that code that exists as COM objects can still be used from the .NET Framework, thanks to wrapper classes that act as bridges between the two platforms. With tools like Visual Studio.NET, the process has been made simple and almost transparent to the user. So, the next time you remember that you have a COM object that implements the functionality you need while developing in .NET, consider the option of COM Interop. In the next part of this series, we’ll look at utilizing .NET assemblies from COM.