C Sharp

Using Callback Functions with C#

Not only can a C# application call a DLL function, but the DLL function can also call designated C# methods in your application in callback scenarios. Callback scenarios comprise use of any of the Win32 EnumXXX functions where you call a function to enumerate something, passing it a function pointer that will be called by Windows with each item that's found. This is done through a combination of PInvoke-to call the DLL function-and delegates-to define your callback. If you need a refresher on delegates, have a look at Chapter 14, "Delegates and Event Handlers." -

The following code enumerates and prints out the captions of all the windows in the system: -

using System;
using System.Runtime.InteropServices;
using System.Text;
class CallbackApp
{
 [DllImport("user32.dll")]
 static extern int GetWindowText(int hWnd, StringBuilder text, int
 count);
 delegate bool CallbackDef(int hWnd, int lParam);
 [DllImport("user32.dll")]
 static extern int EnumWindows (CallbackDef callback, int lParam);
 static bool PrintWindow(int hWnd, int lParam)
    {
        StringBuilder text = new StringBuilder(255);
        GetWindowText(hWnd, text, 255);
        Console.WriteLine("Window caption: {0}", text);
        return true;
    }
    static void Main()
    {
        CallbackDef callback = new CallbackDef(PrintWindow);
        EnumWindows(callback, 0);
    }
}

First I define the Win32 functions EnumWindows and GetWindowText by using the DllImport attribute. I then define a delegate called CallbackDef and a method named PrintWindows. After that all I need to do in Main is instantiate the CallbackDef delegate (passing to it the PrintWindows method) and call the EnumWindows method. For each window found in the system, Windows will call the PrintWindows method.

The PrintWindows method is interesting because it uses the StringBuilder class to create a fixed-length string that is passed to the GetWindowText function. This is why the GetWindowText function is defined as follows: -

static extern int GetWindowText(int hWnd, StringBuilder text, int count);

Anyway, the reason for all this is that the DLL function is not permitted to alter a string, so you can't use that type. And even if you attempt to pass by reference, there's no way for the calling code to initialize a string to the correct size. That's where the StringBuilder class comes in. A StringBuilder object can be dereferenced and modified by the called function, as long as the length of the text does not exceed the maximum length passed to the StringBuilder constructor.