- .NET Interoperability
- Abstract
- Prerequisite
- COM
- .NET Interoperability
- Runtime Callable Wrapper
- COM Callable Wrapper
- ActiveX
- Conclusion
Abstract
This article begins by taking a brief backward glimpse at COM then compares it with the way that components interact in .NET and finally it takes a look at the tool provided by Microsoft to let these two technologies coordinate. Essentially, COM is the antecedent technology to . NET. COM defines a component prototypical where components can be written in dissimilar programming languages. In addition to that, they can be cast off inside a process, across a process or across the network (DCOM). But the COM technology became more and more complicated in terms of implementation and turned out not to be extensible enough. However, .NET justifies similar goals as COM but introduces new conceptions to make the job easier.
Prerequisites
In order to implement COM components interoperability with the .NET Framework, the following are required:
- Visual Studio 6 with VC++ (optional)
- Visual Studio 2010 with VC++ .Net features
COM
Before delving into .NET-COM interoperability, it is important to be aware of the legacy Component Object Model (COM) itself. COM was Microsoft's first attempt at creating a language-independent standard for programs. The idea was that interfaces between components would be defined or invoked according to a binary standard. This would have inferred that you could invoke a VC++ component from a VB application and vice versa. COM components implement one or more interfaces that define the various methods that an application may invoke during marshaling.
COM in Practice
To see how a .NET application can use a legacy COM component, you first need to create a COM component using VC++ because creating a COM component is not possible with C# or VB.Net.
- To create a COM component with ATL and C++, create a new ATL project. Here set the name to comServerTest and with the application setting, select Dynamic Linking Library and finally click Finish.
- The first thing you need to define is a class, mathLib, then place the methods Addition() and Square() definitions in the ImathLib interface. Once we have done with this, look at the comServerTest.idl file that shows all the method definitions as in the following.
// comServerTest.idl: IDL source for comServerTest
// This file will be processed by the MIDL tool to
// produce the type library (comServerTest.tlb) and marshalling code.
import "oaidl.idl";
import "ocidl.idl";
[
object,
uuid(DFC4EDDD-1115-4B36-90A3-15F547018FE8),
dual,
nonextensible,
pointer_default(unique)
]
interface ImathLib : IDispatch{
[id(1)] HRESULT Addition([in] LONG val1, [in] LONG val2, [out,retval] LONG* val);
[id(2)] HRESULT Square([in] LONG val1, [out,retval] LONG* val);
};
[
uuid(F2DBF0C9-8136-4F38-BED2-333A365ED980),
version(1.0),
]
library comServerTestLib
{
importlib("stdole2.tlb");
[
uuid(C670DD23-8B50-445D-9E53-17D6248EF1DA)
]
coclass mathLib
{
[default] interface ImathLib;
};
};
- Now implement the business logic of those methods into the mathLib.cpp class as.
STDMETHODIMP CmathLib::Addition(LONG val1, LONG val2, LONG* val)
{
*val = val1 + val2;
return S_OK;
}
STDMETHODIMP CmathLib::Square(LONG val1, LONG* val)
{
*val = val1 * val1;
return S_OK;
}
- Finally, build the component. The build process also configures the component in the registry.
- If you are developing your client .NET application on the same machine, you don't need to manually register it. However, if you are working entirely on a new machine, you need to register this component with the regsvr32.exe utility as in the following.
.NET Interoperability
The mechanism where data is passed from COM components to .NET and vice-versa is called marshaling. The foremost concern during this process is the conversion of passed data types because both technologies have different data type definition schemes. But .NET and COM have a common representation for some data types such as int, long, short, and byte where no conversion is required during marshaling called Blittable data types.
In nonblittable data types, conversion is required during the data pass because these data types have a higher overhead. The following table lists some nonblittable to related blittable types.
You can use the previously created COM component comServerTest.dll from within the .NET application. We will implement the COM components methods within a Windows Form project. Here, first, we add the reference of the COM component and then instantiate the MathLib class to invoke the methods.
- Create a C# Window Forms project named comServerTestClient.
- Design the form with the appropriate controls as in the following.
- Add the COM component reference as in the following.
- Place the following code for both button click events as in the following.
using System;
using System.Windows.Forms;
using comServerTestLib;
namespace comServerTestClient
{
public partial class Form1 : Form
{
mathLib obj = new mathLib();
public Form1()
{
InitializeComponent();
}
private void btnAdd_Click(object sender, EventArgs e)
{
int x = Int32.Parse(txtX.Text);
int y = Int32.Parse(txtY.Text);
txtResult.Text = obj.Addition(x, y).ToString();
}
private void btnSquare_Click(object sender, EventArgs e)
{
int x = Int32.Parse(txtX.Text);
txtResult.Text = obj.Square(x).ToString();
}
}
}
- Finally, compile and run the application.
Runtime Callable Wrapper
In the preceding example, we have done many activities. Every time you create a default interop assembly by importing a COM DLL into Visual Studio. So, it might be better to do a wrapping once and then let the application import the resulting .NET assembly instead of a COM component. The RCW would spare the hassle of repeatedly dealing with COM characteristics because it hides the unknown and IDispatch interfaces and deals with the reference count of the COM object.
RCW can be created by using the command line utility tlbimp.exe. Here, we are creating a new.dll from the COM component that contains a .NET assembly with the wrapper class as in the following.
COM Callable Wrapper
So far, we have seen how to access a COM component from a .NET application with RCW. Equally interesting is to find a solution for accessing a .NET component from the COM client using a COM Callable Wrapper (CCW).
In the following example, we create a simple C# class library project. Then add an interface to define methods that are invoked from a COM client.
using System;
using System.Runtime.InteropServices;
namespace NETcomponent
{
[ComVisible(true)]
public interface ITest
{
string Hello(string msg);
}
public class Test : ITest
{
public string Hello(string msg)
{
return "Hello " + msg;
}
}
}
Now compile this class library project. Before the .NET components can be used as a COM object, it is necessary to configure it in the registry. You can use the regasm.exe utility to configure the components inside the registry as in the following.
Now open the Visual Studio command prompt to create a COM wrapper class using the tlbexp.exe utility as in the following.
As you notice in the directory of the solution, the NewLib.tlb file is created after executing the tlbexp.exe command. You can view the type library with the OLE/COM object Viewer utility that resides in Visual Studio Tools as in the following.
In the generated code, you can see that the interface ITest is defined as a COM interface, and all the methods defined in the C# code are listed here in the type library.
Active-X
ActiveX controls are COM objects with a user interface. They are used by many containers, such as Word, Excel, IE, and Windows Forms. Similarly, to RCW, we can also create a wrapper for ActiveX controls using the aximp.exe command-line utility.
Conclusion
In this article, you have learned how the .NET application interoperates with COM and vice-versa using RCW and CCW. You also come across a couple of underlying utilities such as tlbimp to import a type library, tlbexp to export a type library from .NET assemblies, and regasm and regsvr32 to register a .NET component in the registry. You also incorporate the ActiveX control into a .NET user interface.