To call most API/DLL functions from Delphi, you write a function that calls the API function, and you're done. In some cases, however, system needs to call an application-defined function in response to events generated by an API call.
Simply put, a callback function is a routine in your program that Windows calls. More generally, a callback is a means of sending a function as a parameter into another function. When the callback function has completed, control is passed back to the original function.
We never call the callback function directly; the API function calls it for us. This allows Windows to communicate directly with the application, passing it various parameters as defined by the individual callback function.
A good example of a Windows API functions that require callback functions are enumeration functions, which enumerate through a group of Windows objects: EnumWindows, EnumPrinters, EnumFontFamilies, and so on.
Other functions that require callback functions are often functions that must execute some action repeatedly.
How do I know a Callback is required?We can determine whether an API function requires a callback function by looking at its parameters. Parameter that takes a pointer to a callback function is a long pointer, usually prefaced with the prefix "lp". Also, the name of the parameter usually ends in "Func", indicating that it takes a pointer to a function.
For example, take a look at the declaration for the EnumWindows function defined in the Windows.pas unit. The lpEnumFunc parameter indicates that the function requires a callback function.
Individual callback functions have specific parameters that must be declared by the application. This is required so that Windows passes the correct information to the application in the correct order. In order to know what the callback function should look like we'll have to look in the documentation for the function (Win'32 Programmer's Reference).
function EnumWindows(lpEnumFunc: TFNWndEnumProc; lParam: LPARAM): BOOL; stdcall;
The EnumWindows function enumerates all top-level windows on the screen by passing the handle of each window. For each window enumerated, the EnumWindows function will call back to a function in your application, passing back information about that window. EnumWindows continues until the last top-level window is enumerated or the callback function returns False, meaning that you wish to stop further enumerations.
Example: Enumerating All WindowsTo demonstrate the usage of callback functions we'll do the following: retrieve the window title (Caption) of each window on the screen by using 'GetWindowText' API call; modify it by using the 'SetWindowText' function. Even more: we'll list all those windows (captions) in a Memo component.
We name the callback function whatever we want to; I suggest you to use the name of the API function, followed by "Func". For example, the callback function of the EnumWindows function could be EnumWindowsFunc. Of course, we can have more than one one callback function for the same API function.
We'll declare our callback function like this:
Note: without the stdcall directive, Windows will not be able to access the callback function. This ensures parameters are passed in the correct order.
function EnumWindowsFunc (Handle: THandle; List: TStringList): boolean; stdcall;
Most Windows API functions that accept a callback function as a parameter will also accept some kind of "application defined data". Take a look at LParam parameter of the EnumWindows function. We are free to define what this parameter is used for. The LParam parameter we pass to the Windows API function will be passed back to our callback function.
Place one Memo and one Button component on a form. Assign the next procedure to the OnClick event handler of a Button.
Note: since EnumWindows needs a pointer to our callback function, we use the @ operator, to send the address of the EnumWindowsFunc callback function in memory.
procedure TForm1.Button1Click(Sender: TObject) ; begin Memo1.Clear; EnumWindows(@EnumWindowsFunc, LParam(Memo1.Lines)) ; end;
All we need now, is the implementation of our callback function:
EnumWindowsFunc adds each existing (<>'') parent's window caption to the List variable that is of a TStringList type. In order to add window captions to the List, the TStringList object is passed into the callback function in the lParam parameter. The callback function calls two other API functions: the GetWindowText API function, which copies the text of the specified window's title bar (if it has one) into a buffer (Caption), and the SetWindowText function, which in turn changes the text of the specified window's caption.
function EnumWindowsFunc(Handle: THandle; List: TStringList) : boolean ; stdcall; var caption: array[0..256] of Char; begin if GetWindowText (Handle, Caption, SizeOf(Caption)-1) <> 0 then begin List.Add(Caption) ; SetWindowText(Handle, PChar('About - ' + Caption)) ; end; result :=True; end;
LParam - Application Defined Data
As stated earlier, we are free to define what this parameter is used for. Also, we don't have to pass any value to lParam at all (that is, we pass a zero value). However, by passing the Memo1.Lines we have to possibility to work with any string list from our callback function. Take a look at this code
The List variable is of a TStringList type. Without changing any code in the callback function, we can use the EnumWindowsFunc function to fill in the ListBox or ComboBox component (Items property) we just have to change the call to the EnumWindows function, that is the LParam parameter we are sending.