At the code camp yesterday I was approached with a PInvoke question on how to handle a cross-unmanaged/managed function callback. Back in my Win32 days (which is now some years on the past of doing native Win32 programming on a daily basis) this was common place, but I've not had this need in the .NET world - though the need does exist, as demonstrated by this gentleman's circumstances.
I took the task of figuring it out right there and quickly came up with this example that illustrates the technique that I took.
This example uses the frequently illustrated Win32 function EnumChildWindows to enumerate the child windows of a given window (e.g. controls on a form). Sure, you can accomplish this by simply enumerating the .Controls collection on the form, but that's not what we're illustrating here - we want to do it via Win32 and a function callback.
The EnumChildWindows function accepts as a parameter a function pointer which will be called-back by the Win32 function once for each window found. Once the callback returns FALSE (i.e. zero) the enumeration stops.
Setting up this function in managed code is trivial:
[SuppressUnmanagedCodeSecurity()]
private static class NativeMethods {
internal delegate int EnumChildProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
internal static extern int EnumChildWindows(
IntPtr hWnd,
[MarshalAs(UnmanagedType.FunctionPtr)] EnumChildProc fn,
IntPtr lParam);
}
Notice that in order to make the callback work I have to decorate the function callback parameter with the [MarshalAsAttribute] designating that as a FunctionPtr.
To test this functionality I created a simple form, added some controls (including a ListBox and Button). I then named the button btnEnum and the ListBox lstOutput. Then I wrote a little code in the Click event handler to call the unmanaged function, providing it with a callback function (via my delegate instance). The callback function resolves the hWnd (window handle) of the window to the control and outputs the control name to the list box as follows:
private void btnEnum_Click(object sender, EventArgs e) {
lstOutput.Items.Clear();
NativeMethods.EnumChildWindows(this.Handle, enumChildren, IntPtr.Zero);
}
private int enumChildren(IntPtr hWnd, IntPtr lParam) {
Control ctrl = Control.FromChildHandle(hWnd);
lstOutput.Items.Add(ctrl.Name);
return 1; // indicate that we want to continue enumerating until we reach the end
}
That's about it...there's not much to it :)