Interoperability feature in .NET Framework and how to communicate with a COM component from .NET

The article describes the interoperability feature in .NET Framework and how to communicate with a COM component from a .NET managed application.

Summary

This whitepaper will provide you a enhanced understanding of what is Interoperability feature in .Net framework and how to communicate with a COM component from a .NET managed application. It moreover gives a brief idea on how to communicate with other managed languages (such as VC#, VB.NET, J# etc).

I assume that the reader has the essential knowledge of COM and .NET.

Introduction

The word ‘Interoperability’ really refers to the ability of diverse systems and organizations to work together (inter-operate). This term is often used in technical systems engineering cases also. In computer systems, there is lot of interaction happening between different new systems and old systems.

The .NET Framework provides many great features including the Interoperability; it is achieved by set of tools and services provided by the framework. It includes a large library and supports several programming languages like C#, VB.NET etc. which allows language interoperability (every .NET compatible language classes can communicate each other).

The .NET components can communicate with the existing COM components without migrating to those components into .NET. that means, this feature is a great help to reduce the migration cost and business systems. Going forward, I’ll explain it in detail.

PIAs (Primary Interop Assembly) provide the information that supports interoperability between .NET and COM. .NET 4 can give you some significant benefits through the "NoPIA" feature (also called Type Embedding, but "NoPIA" just sounds cooler). With NoPIA, the necessary information to support COM interoperability is embedded into your compiled .NET code. Not only does that reduce the number of components to be distributed, only the parts of the PIA actually required by your application are incorporated into your code. If you only use a small part of a big COM object you could see significant size reductions in your compiled code.

To enable the PIAs feature in your project, select the reference to your COM object. Then, in the Properties window, find the Embed Interop Types property and set it to True.

How to make Interoperability between Managed code

As we most of them knows, we cannot directly add a C# (.cs) class file into a VB.NET project and vice versa. Fortunately, we have one another technique to use the functions from the .cs class in the VB.NET project.

First create the dynamic link library (DLL) for the .cs class file and add the DLL reference into the VB.NET project where we want to use ti. After making the reference in the current project, import the namespace for the DLL. Then, all the exposed methods will be available in this project.

Moving forward, I have explained the interoperability with the COM components later in this paper.

Set the project output type as Class Library (DLL)

To create the project output as a class library file, follow the below steps:

Create a new Visual Studio project.

Right click on project properties/select the project properties from the project main menu (from the top).

Click on the Application tab and select Output type as Class Library.

Please make sure that, the build platform target is point to ‘any CPU’ so that this library could run on both the 32 and 64 bit machines.

Fig.1 project properties window to set output type as Class Library.

Write down all the business logic in the class files and do a build on the project. The build will generate the corresponding DLL in the bin folder of the project. The name of the DLL will be same as the Assembly name given in the above application tab (FaceTracker_JeneeshSDK).

Once the library is ready, get the .dll from the bin directory and keep it in the vb.net project related directory for easy handling (you can keep it anywhere). Make a DLL reference from the new project to the library and import the library namespace in the class file, where we are going to use the methods from the library.

Fig. 2 Add DLL by Add Reference method.

Interoperability with Un-Managed code or COM components

Platform invoke enables managed code to call functions exported from an unmanaged dynamic link library (DLL), such as Win32 API and custom DLLs. The CLR handles DLL loading and parameter marshaling. Fortunately, switching from COM to .NET involves no such radical loss of productivity. The concept of providing bridge between .NET and COM components is .NET-COM interoperability.

Assembly is a collection of types and resources that are built to work together and form a logical unit of functionality. All the information related to the assembly will be held in assembly metadata.

Configurations to use COM components in Managed code

COM is a binary reusable object which exposes its functionality to other components.

Place all the dependency files and the COM library in any of the folder and update the path to the folder on system properties->environment variables path. If we doing this configuration, we don’t need to mention the entire DLL path on the DllImport attribute.

If we are not making this configuration on environment path settings, we have to mention the full DLL path on the DllImport attribute.

Follow the below steps to set the environment path in your machine:

Go to my computer properties.

Click on advanced system settings from the left pane.

Go to Advanced tab on the system properties window.

Click on Environment variable button on the bottom of the window.

On system variable section, select the path variable and click on Edit and paste the DLL path on the variable value text box (;c:\jeneesh\myCOM\).

Integrate C++ DLL in C# project

I hope, you people might be aware that we cannot add the COM libraries directly into the project by using add reference method. Each COM component will expose a set of interfaces through which the communication between COM components will occurs.

The following diagram shows the communication between a client and a COM object.

Fig.4 Communication between client and a COM object

Usually COM components will expose interfaces to communicate with other objects. Since a .NET client cannot directly communicate with a COM component, the interfaces exposed by the COM are also not understandable by the .NET applications. To accomplish this communication, the COM component should be wrapped in such a way that the .NET client application can understand the COM component, which is called by the term Runtime Callable Wrapper (RCW).

The Runtime Callable Wrapper (RCW) provided by the .NET Framework which wraps the COM components and exposes it into the .NET client application. A Runtime Callable Wrapper (RCW) is very important to communicate a .NET client with COM. It can be generated by using Visual Studio .NET or by using the TlbImp.exe utility.

Fig.5 calling a COM component from .NET client

In the reverse case, to communicate with the .NET component from the COM component, the .NET component should be wrapped in such a way that the COM client can identify this .NET component. This wrapper is known as COM Callable Wrapper (CCW). CCW will be created by the .NET utility RegAsm.exe. This reads metadata of the .NET component and generates the CCW. This tool will make a registry entry for the .NET components.

For example, the below line of code is a simple COM function. Here, I’ll explain you how to use this method in a C# .NET project.

EXTERN_C ASDK constchar* WINAPI CX_GetLastError();

This above simple function takes zero arguments and returns const char* type value. The types are totally different in COM and .NET. For any kind of pointers, .NET has a type IntPtr, which holds the memory address for that object. By Marshalling, we can obtain the value from the memory address.

If we want to write non managed code in .net, it has to be embed in unsafe block and on the project properties build tab, check allow unsafe code to true.

Now Design C# program for using the DLL created in C++ languages (COM). First of all we need to use the InteropServices namespace to access the dll written in C++ languages. Take a look at below program to learn how to use dll for accomplishing the Interoperability feature.

Code explanation: To start with the wrapper methods, first we need to import the namespace System.Runtime.InteropServices. All the wrapper methods has to precede with the ‘DllImport’ attribute and its different parameters. If we did not set the environment path for the DLL location, we need to mention the full path to the DLL. EntryPoint is optional, CharSet will be ANSI, and the calling convention will the standard call. Methods will be static.

From given code you can see how we are able to see the function written from C++ languages to C# languages. its all happen just because of Interoperability supported by .NET framework.

Fig.7 Implement wrapper method in .NET project.

Code explanation: the return type of the CX_GetLastError() method is a const char* type in C. Since we don’t have pointers in .NET, we have to use IntPtr type in place of const char*. It actually gives the memory location, where the value has been stored. By doing a Marshal on the Intptr, we can retrieve the data stored on this memory location. Once we extracted the data from the pointer, we need to free the memory allocated to the pointer.

The below table describes some of the Win32 types and their corresponding CLR types.

Conclusion

Communication between COM applications and .NET applications can be achieved through Runtime Callable Wrapper (RCW) and COM Callable Wrapper (CCW).

As we have seen in the above example, we can use the methods in a COM component in .NET applications.

When we are playing with unmanaged code through COM interoperability, we cannot use all the features provided by the .Net framework (e.g., inheritance, parameterized constructor etc).

You explain COM but your sample is P/Invoke. This seems to be similar but are two pairs of shoes. COM components can be referenced like any other .NET assmbly via the "add reference" dialog. Visual Studio automatically generates a wrapper dll (normally named Interop.<component-name>.dll). There is no need to add a path to the environment as all information of a COM component can be obtained from the Registry.