Interoperating with Unmanaged Code - PInvoke
Introduction
At the time .NET Framework is firstly introduced by
Microsoft, the concept of managed code and unmanaged code as two different
programming models is introduced as well.
Microsoft defines that managed code is the code generated by the .NET
Framework and could be executed by the common language runtime (CLR). At the
other hand the unmanaged code is any other code that doesn't match the pervious
definition. As a result, all the code created and generated before the .NET
Framework is released considered unmanaged code. This unmanaged code
contains WIN32 APIs, valuable external libraries, COM components, and COM+
services, and all of these are so useful and important.
The dilemma now is: the .NET Framework which is the
current development environment only accepts managed code. We have already made
valuable libraries and components but all are unmanaged code. We need to use
this valuable unmanaged code while we are working under the .NET Framework
environment. What is the solution of this dilemma ???
To solve this problem and for backward compatibility
Microsoft fires another concept and calls it "Interoperating with unmanaged
code". Which is how to call or use unmanaged code form within managed code and
vice versa. Then it divided this process into two categories which are:
-
- How to call WIN APIs and DLLs (unmanaged code) form
within the .NET Framework (managed code) using the Platform Invoke (PInvoke) technique.
-
- How to use COM components (unmanaged code) from
within the .NET Framework (managed code) using the COM Interop technique.
The first item in the pervious list is the subject of
this tutorial. We will try to highlight the main differences between unmanaged
and managed code. Then we will create an example showing how to call unmanaged
DLL function from within the .NET Framework managed code using the platform
invoke technique.
To download the example created in this tutorial just
click here.
Dissimilarities Between managed and unmanaged Programming Models
Managed and unmanaged programming models are dissimilar
in many aspects. The following paragraphs highlight some of those aspects with a
brief description about each of them.
Type definition or information is mandatory for
all types in the managed programming model while it is optional for only public
types in the unmanaged programming model. Managed model use metadata for type
definition while unmanaged model use type libraries. Interoperating services
tools convert type libraries to metadata in assemblies and metadata to type
libraries to overcome the interoperation process.
Type safety is highly achieved in managed code
rather than unmanaged code. The unmanaged programming model is not type safe
while the managed one is optionally type safe. In unmanaged model compilers
provide no type checking on pointer types which may lead to potentially harmful
activity. You can still use pointers in managed code but this will put
restrictions on that code due to its unsafe behavior.
It will be marked as untrusted code. Interoperation services prevent untrusted managed code form
accessing unmanaged code.
Type compatibility is not achieved between managed
and unmanaged code. Types differ between the two models and also among different
programming languages.
Coding models are differ. The unmanaged objects
always communicate via interfaces, while managed objects can pass data directly
without implementing interfaces. Interoperating services especially COM interop
generates interfaces to the managed objects to expose their functionalities to
COM objects.
Identity used in unmanaged model is GUID, while
managed model uses strong names. Unmanaged model uses the GUID to identify a
specific type. Managed model uses strong name consists of a unique assembly name
and a type name for the same purpose. Interoperating services generate GUIDs and
strong names as required.
Error handling mechanism in managed code is
exceptions. COM methods return an HRESULT as a result to indicate wither the
call succeeded or failed. COM interop maps managed exceptions to failure
HRESULT.
Consuming Unmanaged DLL Functions Using PInvoke
Consuming unmanaged DLL function means that we need to
call a function located in an unmanaged DLL library from within the .NET
framework. Platform invoke or PInvoke is the technique used to make this
happens. When platform invoke calls an unmanaged function it firstly locates
the DLL containing the function and then loads it into memory. It searches for
the function in memory and locates its memory address. After that it pushes the
function arguments into the stake and marshaling data as required. Finally
the control is transferred to the unmanaged function. Platform invoke throws
exceptions generated by the unmanaged function to the managed caller.
To call a specific function within a specific DLL you
have to follow the following steps:
-
- First you have to locate this DLL, know its name,
and know the function name you need to call.
-
- After that you have to create a class to hold that
DLL function. You can use an existing class, create a new class for each DLL
function, or collect related DLL functions in one class.
-
- After creating the class (or module in Visual Basic)
you have to create a new managed prototype for that unmanaged function
declared inside the created class.
-
- Finally you are ready to call that DLL
function from within your application.
Let us create a real example to examine the above four
steps in more details.
PInvoke Example
This example aims to call an unmanaged DLL function
exists in the WIN32 APIs library from within a new created Visual Basic
application. We will use the Visual Studio 2005 as the development environment
for our example. So, open up your Visual Studio 2005 and create a new Visual
Basic project. Add a new class for your project and name it "Unmanged".
At the top of the class file add the following imports statement to import the
interoperating services capabilities to our class.
Imports System.Runtime.InteropServices
Now to
step1.
Function and DLL identification
There are three commonly used WIN32 APIs "GDI32.dll" for
graphics device interface (GDI), "Kernel32.dll" for low level operating system
functions, and "User32.dll" for windows management functions for message
handling, menus, timers, and communications. Of course you are free to make use
of any other APIs. You will find them in the "System32" directory under your
windows directory.
For our example we will select the "User32.dll", so the
DLL name is now identified. What about the function?, we have to know functions
inside that DLL to select from. Many tools both command line and visual are
existed to do this task, among them we prefer the visual tool shipped with the
Visual Studio 2005 and called Microsoft dependancy walker "Depends.exe". You can run this tool from
"\Common7\Tools\Bin" directory under your Visual Studio directory. The following
figure shows this tool opening the "User32.dll" library in its left pane, while
the right pane listing all functions located in this library.
 Figure 1 -
Depends.exe" tool lists the functions located at the "User32.dll" library
As the above figure implies we choose the "MessageBox"
function. WIN32 APIs may contain two versions for each function that handles
characters and strings. One for the ANSI version (MessageBoxA), and the other
for the Unicode version (MessageBoxW). By choosing the "MessageBox" as our
function the identification process is completed.
Create a class to hold the DLL function
We already made this step when we add a new class to our
created project and called it "Unmanged". This process is called wrapping.
Wrapping the unmanaged DLL functions in a managed class allows the platform
invoke to handle the underlying exported functions automatically. You have to
define a static method for each DLL function you want to call.
Create prototypes in managed code
In this step we will define the function prototype under
our class using managed model rather than the unmanaged definition exists at the
DLL library. To get the unmanaged prototype of the "MessageBox" function, search
the MSDN library under the "win32 and COM development" section, or enter the
function name in he search box then filter the result to the "Windows platform"
section only. The unmanaged prototype of the "MessageBox" function is as
followes.
int MessageBox(HWND hWnd,
LPCTSTR lpText,
LPCTSTR lpCaption,
UINT uType
);
To set the new managed declaration in our new visual
basic project we will use the "Declare" statement to define the function and the
"Lib" keyword to identify its library name as in the following line of code.
Declare Auto Function MessageBox Lib "User32.dll"
The "Auto" keyword is set to let the target platform
determines the suitable character width (ANSI or Unicode).
Now to complete the declaration process you need to
convert the unmanaged types of the function parameters to their managed
counterparts. MSDN library provides a table that resolve each WIN32 API type into
its managed type. You can get this table from the following link
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconplatforminvokedatatypes.asp
Now convert each unmanaged type into its managed
counterpart using the above table.
Public Class Unmanged
Declare Auto Function MessageBox Lib "User32.dll" _
(ByVal Hwnd As Integer, ByVal Text As String, _
ByVal Caption As String, ByVal Type As Integer) _
As Integer
End Class
That's it. The DLL function now is ready to be called
from within the current project.
Calling the DLL function
To examine the DLL function we will call it from within the
"Form1" class as following.
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
Unmanged.MessageBox(0, "Hello every one", _
"PInvoke Example", 0)
End Sub
End Class
Now run the program and get the results.
 Figure
2 - The unmanaged "MessageBox" function called from within a managed code
As figure2 implies, the function is called and a message
box with the caption and text input parameters is displayed.
To download the example created in this tutorial just
click here.
For further information
Refer to the online copy of Microsoft Developers Network at
http://msdn.microsoft.com or use your own local copy of MSDN.
|