Accessing Code in Unmanaged DLLs

Accessing code in unmanaged DLLs is similar to accessing code using COM InterOp in that your code is reaching across AppDomain boundaries and from managed to unmanaged code. This type of access requires the SecurityPermission of UnmanagedCode. Don't confuse the phrase "unmanaged DLLs" with "COM DLLs." There is a distinct difference. The contents of a COM DLL conform to a specific binary format with specific functions used to query the list of interfaces exposed by the components contained within that DLL. A standard unmanaged DLL has no such standard.

A normal unmanaged DLL has no way of telling consuming clients what functions are available, so clients need to know ahead of time what functions they can use and the size, type, and direction of all function parameters.

You might remember way back in the days when you couldn't expose true object-oriented componentsthe best you could do was export functions within a DLL.

To tell the CLR that you are using a method from an unmanaged DLL instead of a native managed method, you decorate a method declaration with the DllImport attribute. This attribute takes the name of the DLL in which the CLR can find the associated extern function, which is declared right under the DllImport attribute, as shown in the following example:

[DllImport("myFuncs.dll")] public static extern void MyFunction(); 

One of the most common uses for consuming functions from unmanaged DLLs is to gain direct access to the Win32 API functions. The code in Listing 13.2 shows you a common way of consuming unmanaged code in the Win32 API.

Listing 13.2. Using DllImport

using System; using System.Runtime.InteropServices; using System.Collections.Generic; using System.Text; namespace Pinvoke1 { class Program {     [DllImport("user32.dll")]     public static extern int MessageBox(IntPtr hwnd, String text, String caption, uint type);     static void Main(string[] args)     {         MessageBox(new IntPtr(0), "Greetings from Platform Invoke", "Platform Invoke", 0);     } } } 

Before you decide to use unmanaged code directly, make sure that the same functionality is not already available within the .NET Framework. As mentioned earlier, the cost of marshaling data and invoking unmanaged code via wrappers is expensive.

You can also use some functionality that is new to .NET 2.0 to create an instance of a delegate that is really a function pointer to unmanaged code. You can then invoke that delegate instance just as you would any other managed delegate, as shown in Listing 13.3. This provides a far easier interface to calling unmanaged code such as the Win32 API than trying to do it "the hard way."

Listing 13.3. Obtaining and Invoking Unmanaged Function Pointers

using System; using System.Threading; using System.Runtime.InteropServices; using System.Collections.Generic; using System.Text; namespace PInvoke2 { class Program { [DllImport("kernel32.dll")] static extern IntPtr LoadLibrary(string dllName); [DllImport("kernel32.dll")] static extern IntPtr GetProcAddress(IntPtr hModule, string procName); delegate int MessageBoxDelegate(IntPtr hwnd,     [MarshalAs(UnmanagedType.LPWStr)]string text,     [MarshalAs(UnmanagedType.LPWStr)]string caption,     int type); static void Main(string[] args) {     IntPtr userApi = LoadLibrary("user32.dll");     IntPtr msgBoxAddress = GetProcAddress(userApi, "MessageBoxW"); // unicode (wide) message box     MessageBoxDelegate mbd = (MessageBoxDelegate) Marshal.GetDelegateForFunctionPointer(msgBoxAddress,         typeof(MessageBoxDelegate));     mbd(IntPtr.Zero, "Hello from Platform Invoke", "Platform Invoke", 0);     DoSomething(mbd); } static void DoSomething(MessageBoxDelegate mbd) {     Thread.Sleep(200);     mbd(IntPtr.Zero, "Work completed.", "Work Progress", 0) ; } } } 

There are a couple of new things in the preceding code. The first is the use of the Marshal.GetDelegateFromFunctionPointer() method. This method is incredibly powerful. It allows you to take any function pointer represented by an IntPtr retrieved using the GetProcAddress function from the kernel32 library and turn that function pointer into a managed delegate. After you've obtained the delegate from the function pointer, you can create events based on that delegate, you can pass the delegate as a parameter to other functions, and you can invoke the delegate directly.

There is also a new attribute in the preceding code, MarshalAs. This attribute allows you to define, for any method parameter, how that data type should be marshaled. For example, if you know that the function you want to call using a managed delegate takes an LPWSTR (long-pointer to a wide Unicode string), you can marshal a managed string as an UnmanagedType.LPWStr. The marshaler will then take care of the details of converting a managed string into a LPWSTR.

Microsoft Visual C# 2005 Unleashed
Microsoft Visual C# 2005 Unleashed
ISBN: 0672327767
EAN: 2147483647
Year: 2004
Pages: 298 © 2008-2017.
If you may any questions please contact us: