Namespace: System.Runtime.InteropServices
Calling Platform Invoke:
To call an unmanaged Windows API
that is not wrapped in .Net Framework, P/Invoke is the only viable solution.
There are three typical requirements
that intend to use P/Invoke:
1.
Functionality Specific to
OS, Context Switching, and File/IO
2.
Advanced manipulation of
windows, menu, dialogs etc.
3.
Advanced and fast drawing
features
Use the syntax:
[DllImport(“user32.dll”)]
private static extern Int32
GetWindowsText(IntPtr hWnd, StringBuilder textValue, Int32 Count)
Use StringBuilder instead of
String when calling P/Invokes.
Encapsulating DLL Functions:
Try to Encapsulate the P/Invokes in
a specialized class, so that one can use them again. It relives the load of
remembering complete syntax of the OS API.
Converting Data Types:
When interacting with OS through
P/Invokes one must specify the type the target type to which the parameter,
return value, or structure should be converted. One can also apply this
attribute to properties as well.
[MarshalAs(UnmanagedType.LPStr)]
Marshaling Structures:
By default CLR handles the Layout of
the structures and classes. For structures compiler automatically applies LayoutKind.Sequential.
For Sequential Compiler maintains the ordering of the variables. While in LayoutKind.Explict
you must specify the Offset values.
Examples:
[StructLayout(LayoutKind.Explicit,
Size=16, CharSet=CharSet.Ansi)]
public class MySystemTime
{
[FieldOffset(0)]public ushort wYear;
[FieldOffset(2)]public ushort wMonth;
[FieldOffset(4)]public ushort wDayOfWeek;
[FieldOffset(6)]public ushort wDay;
[FieldOffset(8)]public ushort wHour;
[FieldOffset(10)]public ushort wMinute;
[FieldOffset(12)]public ushort wSecond;
[FieldOffset(14)]public ushort wMilliseconds;
}
It is very important especially when target API requires you
to pass a structure; you must perform Sequential or Explicit Byte alignment as
required by the API.
By default CLR applies
LayoutKind.Auto to reference type and LayoutKind.Sequential
to value type objects.
Using Callback with Unmanaged Code:
Unmanaged
callbacks are also supported by .Net framework. To register for a callback
create a delegate exactly similar to the callback.
To implement a callback function
Look at the signature for the EnumWindows function before
going further with the implementation. EnumWindows has the following signature:
BOOL EnumWindows(WNDENUMPROC
lpEnumFunc, LPARAM lParam)
So, as is the case in the following enumeration example, the
callback function completes its work before the call returns and requires no
additional action by the managed caller.
If, however, the callback function can be invoked after the
call returns, the managed caller must take steps to ensure that the delegate
remains uncollected until the callback function finishes. For detailed
information about preventing garbage collection, see Interop Marshaling with
Platform Invoke.
using System;
using
System.Runtime.InteropServices;
public delegate bool CallBack(int
hwnd, int lParam);
public class EnumReportApp {
[DllImport("user32")]
public static extern int EnumWindows(CallBack x, int y);
public static void Main()
{
CallBack myCallBack = new
CallBack(EnumReportApp.Report);
EnumWindows(myCallBack, 0);
}
public static bool Report(int hwnd, int lParam) {
Console.Write("Window handle is
");
Console.WriteLine(hwnd);
return true;
}
}
Exception Handling in Managed Code:
You can’t catch win32 exception in
straight forward manner as in managed. Follow the following steps to include
Exception Management for unmanaged Code.
[DllImport("Kernel32",
ExactSpelling = true, SetLastError = true)]
static extern Boolean
CloseHandle(IntPtr h);
Void UnmanagedCode()
{
CloseHandle(new
IntPtr(-1));
Win32ExceptionManager.CheckAndRaiseException();
}
[DllImport(“kernel32.dll”)]
private static extern Int32
FormatMessage(Int32 dwFlags, Int32 lpSource, Int32 intdwMessageId, Int32
dwLanguageId, ref String lpBuffer. Int32 nSize, Int32 Arguments);
public class Win32ExceptionManager
{
public
static CheckAndRaiseException()
{
Int32
vErrorCode = Marshal.GetLastWin32Error();
RaiseException(vErrorCode);
}
private static void
RaiseException(Int32 pErrorCode)
{
Int32
FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
Int32
FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
Int32
FORMAT_MESSAGE_FROM_STRING = 0x00000400;
String
lpMsgBuffer = String.Empty;
Int32 dwFlags =
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_FROM_STRING;
Int32Return vError =
FormatMessage(dwFlags, 0, pErrorCode, 0, ref lpMsgBuffer, 255, 0); //255 is
message size
If(vError != 0)
{
throw
new System.ComponentModel.Win32Exception(lpMsgBuffer);
}
}
}
You can only use this method to obtain error codes if you
apply the System.Runtime.InteropServices.DllImportAttribute
to the method signature and set the SetLastError field to true.
The process for this
varies depending upon the source language used: C# and C++ are false by
default, but the Declare statement in Visual Basic is true. For additional
information about the GetLastError and SetLastError Win32 API methods, see the MSDN Library.
Limitations of the unmanaged Code:
Performance: Unmanaged Code
runs faster but it does have memory leak and other issues related. Don’t call
unmanaged code from managed code just to gain performance, because marshaling
overheads will reduce this performance automatically.
Type Safety: Unmanaged code
sometimes is not type safe i.e. when void* are used.
Code Security: .Net introduced
new concepts of Imperative and Declarative security that is not present in
Unmanaged Code.
Versioning: Versioning is not
the issue that was before managed code. But one may expect same issue while
interoperating with legacy and unmanaged code.
Comments