Microsoft.NET

……………………………………………….Expertise in .NET Technologies

Managed and Un-Managed Code in .NET

Posted by Ravi Varma Thumati on May 15, 2009

Managed Code

Code that is executed by the CLR is called managed code. Managed code provides information (i.e., metadata) to allow the CLR to locate methods encoded in assembly modules, store and retrieve security information, handle exceptions, and walk the program stack. Managed code can access both managed data and unmanaged data. Managed data—Memory that is allocated and released by the CLR using Garbage Collection. Managed data can only be accessed by managed code

You can think of the runtime as an agent that manages code at execution time, providing core services such as memory management, thread management, and Remoting, while also enforcing strict type safety in the code. The concept of code management is a fundamental principle of the runtime.

Un-Managed Code

 Code that does not target the common language runtime is known as unmanaged code in other words code that runs outside the runtime is called unmanaged code.

The Microsoft .NET Framework promotes interaction with COM components, COM+ services, external type libraries, and many operating system services. Data types, method signatures, and error-handling mechanisms vary between managed and unmanaged object models. To simplify interoperation between .NET Framework components and unmanaged code and to ease the migration path, the common language runtime conceals from both clients and servers the differences in these object models.

Calling Unmanaged Code from .Net

 Alas, it’s impossible to do everything within the .Net framework. To solve your programming problem, you probably need to call code that’s provided as a part of a DLL. Here’s how to achieve that without making some silly mistakes.

Despite the breadth and depth of the .Net Framework, it cannot do everything. There are times when you need to do something that is not covered by the Framework. In my experience, this situation comes up in two contexts. One is when there is some capability that you know is part of the Windows API but is not yet in the .Net Framework. The other is when you are programming an external device whose drivers are written for the non-. Net programmer. The result is the same: in both cases, your program will need to call unmanaged code. More specifically, it needs to call code that is provided as part of a dynamic link library or DLL (a Windows DLL in the first case, a manufacturer-provided DLL in the second).

Windows and device DLLs are almost always written in C. This means that you, the .Net programmer, face two challenges:

  • How do you call functions in an unmanaged code DLL from your managed code .Net program?
  • How do you map the C/Windows data types passed to and returned from the DLL functions to the Common Language Runtime (CLR) types used in .Net?

These questions fall under the general area of interoperability (interop for short). In .Net, or more specifically in the CLR, the most important interop tool is called Platform Invoke (or P-Invoke). P-Invoke is a service that permits managed code to call unmanaged functions that are implemented in DLLs. In the .Net documentation this is referred to as consuming unmanaged DLLs. Let’s take a look at how this is done.

Identifying the DLL Function

Before your managed code can call a DLL function, it must know the name of the function and where it is located — in other words, in which DLL. You do this with the DllImport keyword. This is in the System.Runtime.InteropServices namespace. For an example, I will use the Windows Beep() function, which has the following declaration (in C):

BOOL Beep(
        DWORD dwFreq,       
// Frequency
        DWORD dwDuration   
//Duration in milliseconds
};

To make this function accessible in your managed code, you would write the DllImport statement like this:

[DllImport("Kernel32.dll")]
    static extern Boolean Beep(
        UInt32 frequency, UInt32 duration);

Don’t worry about the data types for now; I’ll get to them soon. But with this DllImport statement in your managed code, you can call the Beep function like any other function:

Beep(1000, 1500);

As is often the case, the return value of Windows API functions is ignored, because it does not provide any useful information. (DllImport has optional parameters that are needed in some situations. You can learn about these in the .Net online documentation.)

Before you can use functions in a DLL, you must know about them. Where can you get this information? For the Windows API, I recommend one of the several reference books that are available. These books provide information about using each API function including its arguments and return type and the DLL it is contained in (The Windows API is contained in a small set of DLLs that are part of the Windows installation). For third party DLLs, the product documentation should contain this information.

As I mentioned, Windows DLLs are part of the Windows installation and are found by the program without any special effort on your part. Nor do you have to include the DLL files in your distribution package. For third party DLLs, it’s best to put them in the application folder where they will be found by the DllImport service. In this case, you do have to include the DLLs in any distribution package you may create.

Mapping Data Types

One of the tasks performed by the P-Invoke service is to marshal data across the managed/unmanaged code boundary. The data types used by DLL functions rarely have the same names as the CLR data types, so it is incumbent on the programmer to determine the correct CLR data types to use. The table below lists the commonly use data types in unmanaged C functions and in the Windows API along with the CLR equivalents.

Unmanaged Windows API types Unmanaged C language types CLR type
HANDLE void* IntPtr
BYTE unsigned char Byte
SHORT short Int16
WORD unsigned short UInt16
INT, LONG int, long Int32
BOOL int, long Int32 or Boolean
UINT unsigned int UInt32
DWORD, ULONG unsigned long UInt32
CHAR char Char
LPSTR, LPCSTR, LPWSTR, LPCWSTR char*, wchar_t* String or StringBuilder
FLOAT float Single
DOUBLE double Double

If those asterisks have you looking for a footnote, perhaps you are not familiar with C programming. The asterisk is used to designate a pointer, a type of data storage in which a variable does not contain the data, per se, but rather contains the memory address of the data. You needn’t be concerned with the details of pointers as long as you use the appropriate CLR type.

You can see now how I came up with the DllImport declaration for the Beep() function. For the BOOL return value, I used the CLR type Boolean, and for the two DWORD arguments I used the CLR type UInt32. With the correct data types in place, the P-Invoke data marshalling works properly, ensuring that the DLL function is passed the correct argument types and that the return value, if used by the calling program, is correct.

Wrapping the DLL Functions

If you are going to use unmanaged DLL functions in your program, it’s a good idea to wrap them in a class. Aside from the advantages of modularity and reusability, providing a class wrapper is advisable because consuming DLL functions can in some cases be prone to errors. Encapsulating the DLL declarations and calls makes your job easier when it comes time to debug whatever problems come up. The general approach is to declare (using DllImport) the function in the class and then create a public method that calls the DLL. Exception handling can be performed within the class. You can create a separate class for each DLL function or, perhaps more conveniently, create a single class that wraps a set of perhaps related DLL functions.

Let’s look at an example. The class shown here encapsulates a single Windows API function, Beep(). To add other functions, simply add a DllImport statement and a public method for each one. Add the class to any project that needs to call the API functions.

using System;
using System.Runtime.InteropServices;
namespace WinAPI
{
public class WindowsAPI
    {
    [DllImport("Kernel32.dll")]
      static extern Boolean Beep(UInt32 duration, UInt32 frequency);
     public void WinBeep(UInt32 duration, UInt32 frequency)
      {
      Beep(duration, frequency);
      }
    }
}

Then in your program, calling the function is a simple matter. You must import the class’s namespace:

using WinAPI;

Then instantiate the class:

WindowsAPI wAPI = new WindowsAPI();

…and finally call the appropriate method:

wAPI.WinBeep(1000, 1500);

Managed code provides many advantages. When it’s possible to create an entire application in managed code it is almost always the best way to go. The world is still full of unmanaged code, however, and sometimes the .Net programmer will need to make use of it.

 

Using Managed Components from Unmanaged Code

Differences between .NET Framework and COM Framework

The .NET framework object model and its workings are different from Component Object Model (COM) and its workings. For example, clients of .NET components don’t have to worry about the lifetime of the object. Common Language Runtime (CLR) manages things for them. In contrast, clients of COM objects must take care of the lifetime of the object. Similarly, .NET objects live in the memory space that is managed by CLR. CLR can move objects around in the memory for performance reasons and update the references of objects accordingly, but COM object clients have the actual address of the object and depend on the object to stay on the same memory location.

Similarly, .NET runtime provides many new features and constructs to managed components. For example, .NET components can have parameterized constructors, functions of the components can have accessibility attributes (like public, protected, internal, and others) associated with them, and components can also have static methods. Apart from these features, there are many others. These include ones that are not accessible to COM clients because standard implementation of COM does not recognize these features. Therefore .NET runtime must put something in between the two, .NET server and COM client, to act as mediator.

The .NET and COM Mediator

.NET runtime provides COM Interoperability wrappers for overcoming the differences between .NET and COM environments. For example, runtime creates an instance of COM Callable Wrapper (CCW) when a COM client accesses a .NET component. In the same way, an instance of Runtime Callable Wrapper (RCW) is created when a .NET client accesses a COM component. These wrappers abstract the differences and provide the seamless integration between the two environments. The following diagram illustrates CCW and RCW.

 managed1

Let’s Do Something Practical

For demonstration purposes, I have created a .NET component in C# named CManagedServer. This component is in the ManagedServer assembly. For client side, I have created a Visual Basic (VB) 6.0-based client that uses the services of our managed server.

Step 1
First, we create the managed server. The following code for CManagedServer is very simple and contains a single method named “SayHello.”

using System;
namespace ManagedServer
{
   public class CManagedServer
   {
     public CManagedServer()
     {}
     public string SayHello(string r_strName)
     {
   string str ;
   str = "Hello " + r_strName ;
   return str ;    
     }
   }
}

For compilation and creation of ManagedServer assembly, use the following command:

csc /out:ManagedServer.dll /target:library ManagedServer.cs
pause

This command will create the ManagedServer.dll file. This is our managed server.

Step 2
Standard COM implementation relies on the Windows registry for looking up the information related to COM components, like CLSID, Interface IDs, the path of the component’s housing (DLL/exe), the component threading model, etc. .NET framework does not depend on the registry and uses metadata for this information. Therefore, we have to generate the COM-compatible registry entries for our managed server so that the COM runtime could instantiate our server. Like tlbimp.exe, there is a tool named regasm.exe. This tool reads the metadata information within an assembly and adds the corresponding COM-compatible registry entries. Classes in the assembly are not COM creatable until they are actually registered in the Windows registry.

Regasm.exe tool can also generate the COM type library for our manager server. We will reference this type library in VB 6.0 client.

The following command can be used for creating the registry entries and type library for our managed server:

regasm ManagedServer.dll /tlb
pause

Following is the output that will be generated after successful execution of the above command:

managed2

 At this point, standard COM registry entries will have been created. Look at the following figure, this shows the registry entries on my PC:

managed3

 Step 3
Now we will create the client of our managed server. I have created a simple client.

Step 2 created a tlb so we will reference it in the VB client. The following figure shows the ManagedServer tlb listed in the “References” dialog.

 managed4

The following is the code that creates the object and calls its SayHello Method.

Private Sub cmdAccessManagedComponent_Click() 
    Dim objServer As New ManagedServer.CManagedServer
    MsgBox objServer.SayHello("15 Seconds reader")
    Set objServer = Nothing
End Sub

The above code is very simple and COM Interoperability wrappers have abstracted all the complexities of accessing .NET components.

Running the UnmanagedClient.exe and pressing the “Access managed component” button, will produce the following output:

 managed5 

Conclusion

This is what we need to do to access .NET components from unmanaged code. If you want to develop some of your application components in .NET, you should consider that many of the features of .NET components are not exposed to COM clients. This will require some design-time decisions and may affect the way you design your object hierarchies (because inheritance hierarchy of .NET components is flattened for COM clients).

One more thing, currently .NET components are accessible to COM clients only. .NET components cannot be accessed from Win32 DLLs or executables. I am not sure whether Microsoft will incorporate this in future.

Key point to remember:

Managed code Means: –

  • Code that is executed by the CLR.
  • Code that targets the common language runtime, the foundation of the .NET Framework, is known as managed code.
  • Managed code supplies the metadata necessary for the CLR to provide services such as memory management, cross-language integration, code access security, and automatic lifetime control of objects. All code based on IL executes as managed code.
  • Code that executes under the CLI execution environment.

Unmanaged code Means: –

  • It is compiled to machine code and therefore executed by the OS directly.
  • It therefore has the ability to do damaging/powerful things Managed code does not.

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: