Covers topics on the Microsoft Certification Exam for the .NET Framework (Exam 70-536, Microsoft .NET Framework - Application Development Foundation)

Saturday, February 7, 2009

Calling unmanaged code from C#

One of the objectives of Exam 70-536 is

Implementing interoperability, reflection, and mailing functionality in a .NET Framework application
  • Call unmanaged DLL functions within a .NET Framework application, and control the marshalling of data in a .NET Framework application.
There are several subtopics in this requirement. The example presented today will cover
  • Create a class to hold DLL functions;
  • Create prototypes in managed code
  • Call a DLL function
  • Call a DLL function in special cases, such as passing structures and implementing callback functions
Many interop examples seem to cover the simplistic case of hello world in a dialog. But the real challenge when calling unmanaged code involves the marshaling data between the two memory spaces. While writing this example I had a few headaches with access violations (unmanaged code going farther than allowed) and a few marshaling errors. Everything was erroring around the lpCustColors array. In the Win32 definition it is listed as a pointer (to an array of COLORREF structures)

Rather than declare the array in managed space and marshal it over, it proved far easier to allocate and fill the array in the unmanaged space. The example below shows how to call the native ColorChooser dialog. The color chooser dialog requires a specific structure to be populated before it is called.

Calling unmanaged code from C# Example

using System;
using System.Runtime.InteropServices;

namespace InteropExample
{
public class Win32
{
[DllImport("comdlg32.dll")]
public static extern bool ChooseColor(CHOOSECOLOR pChooseColor);

[DllImport("Kernel32.dll")]
public static extern IntPtr LocalAlloc(int flags, int size);

[DllImport("Kernel32.dll")]
public static extern int LocalFree(IntPtr addr);
}

[StructLayoutAttribute(LayoutKind.Sequential)]
public class CHOOSECOLOR:IDisposable
{
public Int32 lStructSize;
public IntPtr hwndOwner;
public IntPtr hInstance;
public Int32 rgbResult;
public IntPtr lpCustColors;
public uint Flags;
public Int32 lCustData = 0;
public IntPtr lpfnHook;
public IntPtr lpTemplateName;

public CHOOSECOLOR()
{
lStructSize = System.Runtime.InteropServices.Marshal.SizeOf(this);
hwndOwner = IntPtr.Zero;
hInstance = IntPtr.Zero;
rgbResult = 0; //black
lpCustColors = Win32.LocalAlloc(64, 64);
//Fill up some random custom colors, just to show we can write to unmanaged memory
for(int i = 0; i <16; i++)
{
System.Runtime.InteropServices.Marshal.WriteInt32((IntPtr)(lpCustColors), sizeof(UInt32) * i, 0x0004937E << i);
}
Flags = 0;
lCustData = 0;
lpfnHook = IntPtr.Zero;
lpTemplateName = IntPtr.Zero;
}

public virtual void Dispose()
{
Win32.LocalFree(lpCustColors);
}
}

class Program
{
static void Main(string[] args)
{
CHOOSECOLOR chooseColorData = new CHOOSECOLOR();
Win32.ChooseColor(chooseColorData);
}
}
}


Results


Additional Resources
.NET Platform Invoke Examples (Microsoft)
Win32 ChooseColor Function (Microsoft)
Win32 Type Mapping between C++ and C# (Code Project)

2 comments:

IT Training said...

Good. Thanks for the sharing. Need some more inputs check this.

anurag srivastava said...

This seems to be helpful.

Support This Site

LinkShare  Referral  Prg