.NET Interoperability at a Glance 3 - Unmanaged Code Interoperation

This article is available also in my blog, check it out here.

Overview

This is the last article in this series, it talks about unmanaged code interoperation; that's, interop between .NET code and other code from other technologies (like Windows API, native libraries, COM, ActiveX, etc.)

Be prepared!

Introduction

Managed code interop wasn't so interesting, so it's the time for some fun. You might want to call some Win32 API functions, or it might be interesting if you make use of old, but useful, COM components. Let's start!

Unmanaged Code Interop

Managed code interoperation isn't so interesting, but this is. Unmanaged interoperation is not easy as the managed interop, and it's also much difficult and much harder to implement. In unmanaged code interoperation, the first system is the .NET code; the other system might be any other technology including Win32 API, COM, ActiveX, etc. Simply, unmanaged interop can be seen in three major forms:

  1. Interoperation with Native Libraries.
  2. Interoperation with COM components.
  3. Interoperation with ActiveX.

Interop with Native Libraries

This is the most famous form of .NET interop with unmanaged code. We usually call this technique, Platform Invocation, or simplyPInvoke. Platform Invocation or PInvoke refers to the technique used to call functions of native unmanaged libraries such as the Windows API.

To PInvoke a function, you must declare it in your .NET code. That declaration is called the Managed Signature. To complete the managed signature, you need to know the following information about the function:

  1. The library file which the function resides in.
  2. Function name.
  3. Return type of the function.
  4. Input parameters.
  5. Other relevant information such as encoding.

Here comes a question, how could we handle types in unmanaged code that aren't available in .NET (e.g. BOOL, LPCTSTR, etc.)?

The solution is in Marshaling. Marshaling is the process of converting unmanaged types into managed and vice versa (see figure 1.) That conversion can be done in many ways based on the type to be converted. For example, BOOL can simply be converted to System.Boolean, and LPCTSTR can be converted to System.String, System.Text.StringBuilder, or even System.Char[]. Compound types (like structures and unions) are usually don't have counterparts in .NET code and thus you need to create them manually. Read our book about marshaling here.

Fig1.gif

Figure 1 - The Marshaling Process

To understand P/Invoke very well, we'll take an example. The following code switches between mouse button functions, making the right button acts as the primary key, while making the left button acts as the secondary key.

In this code, we'll use the SwapMouseButtons() function of the Win32 API which resides in user32.dll library and has the following declaration:

BOOL SwapMouseButton(
BOOL fSwap
);

Of course, the first thing is to create the managed signature (the PInvoke method) of the function in .NET:

// C#
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern bool SwapMouseButton(bool fSwap);

' VB.NET
Declare Auto Function SwapMouseButton Lib "user32.dll" _
    (
ByVal fSwap As Boolean) As Boolean

Then we can call it:

// C#
public void MakeRightButtonPrimary()

{     
SwapMouseButton(
true);
}
public void MakeLeftButtonPrimary()

{
     SwapMouseButton(
false);
 }
' VB.NET
Public Sub MakeRightButtonPrimary()
    
SwapMouseButton(
True)
End Sub
Public Sub MakeLeftButtonPrimary()
    
SwapMouseButton(
False)

End Sub

Interop with COM Components

The other form of unmanaged interoperation is the COM Interop. COM Interop is very large and much harder than P/Invoke and it has many ways to implement. For the sake of our discussion (this is just a sneak look at the technique,) we'll take a very simple example.

COM Interop includes all COM-related technologies such as OLE, COM+, ActiveX, etc.

Of course, you can't talk directly to unmanaged code. As you've seen in Platform Invocation, you have to declare your functions and types in your .NET code. How can you do this? Actually, Visual Studio helps you almost with everything so that you simply to include a COM-component in your .NET application, you go to the COM tab of the Add Reference dialog (figure 2) and select the COM component that you wish to add to your project, and you're ready to use it! 
Fig2.JPG

Figure 2 - Adding Reference to SpeechLib Library

When you add a COM-component to your .NET application, Visual Studio automatically declares all functions and types in that library for you. How? It creates a Proxy library (i.e. assembly) that contains the managed signatures of the unmanaged types and functions of the COM component and adds it to your .NET application.

The proxy acts as an intermediary layer between your .NET assembly and the COM-component. Therefore, your code actually calls the managed signatures in the proxy library that forwards your calls to the COM-component and returns back the results.

Keep in mind that proxy libraries also called Primary Interop Assemblies (PIAs) and Runtime Callable Wrappers (RCWs.)

Best mentioning that Visual Studio 2010 (or technically, .NET 4.0) has lots of improved features for interop. For example, now you don't have to ship a proxy/PIA/RCW assembly along with your executable since the information in this assembly can now be embedded into your executable; this is what called, Interop Type Embedding.

Of course, you can create your managed signatures manually, however, it's not recommended especially if you don't have enough knowledge of the underlying technology and the marshaling of functions and types (you know what's being said about COM!)

As an example, we'll create a simple application that reads user inputs and speaks it. Follow these steps:
  • Create a new Console application.
  • Add a reference to the Microsoft Speech Object Library (see figure 2.)
  • Write the following code and run your application:
// C#
using SpeechLib;
static void Main()

{     
Console.WriteLine(
"Enter the text to read:");     
string txt = Console.ReadLine();     
Speak(txt); }
static void Speak(string text)

{     
SpVoice voice =
 
new SpVoiceClass();     
voice.Speak(text, SpeechVoiceSpeakFlags.SVSFDefault);
}
 
' VB.NET
Imports SpeechLib
Sub Main()
    
Console.WriteLine(
"Enter the text to read:")     
Dim txt As String = Console.ReadLine()     
Speak(txt)
End Sub
Sub Speak(ByVal text As String)
    
Dim voice As New SpVoiceClass()     
voice.Speak(text, SpeechVoiceSpeakFlags.SVSFDefault)
End Sub
 
If you are using Visual Studio 2010 and .NET 4.0 and the application failed to run because of Interop problems, try disabling Interop Type Embedding feature from the properties on the reference SpeechLib.dll.

Interop with ActiveX Controls


ActiveX is no more than a COM component that has an interface. Therefore, nearly all what we have said about COM components in the last section can be applied here except the way we add ActiveX components to our .NET applications.

To add an ActiveX control to your .NET application, you can right-click the Toolbox, select Choose Toolbox Items, switch to the COM Components tab and select the controls that you wish to use in your application (see figure 3.) 
Fig3.JPG

Figure 3 - Adding WMP Control to the Toolbox

Another way is to use the aximp.exe tool provided by the .NET Framework (located in Program Files\Microsoft SDKs\Windows\v7.0A\bin) to create the proxy assembly for the ActiveX component:

aximp.exe "C:\Windows\System32\wmp.dll"

Not surprisingly, you can create the proxy using the way for COM components discussed in the previous section, however, you won't see any control that can be added to your form! That way creates control class wrappers for unmanaged ActiveX controls in that component.

Summary


So, unmanaged code interoperation comes in two forms: 1) PInvoke: interop with native libraries including the Windows API 2) COM-interop which includes all COM-related technologies like COM+, OLE, and ActiveX.To PInvoke a method, you must declare it in your .NET code. The declaration must include 1) the library which the function resides in 2) the return type of the function 3) function arguments.COM-interop also need function and type declaration and that's usually done for you by the Visual Studio which creates a proxy (also called RCW and PIA) assembly that contains managed definitions of the unmanaged functions and types and adds it to your project.