Callback functions are a powerful tool, giving great flexibility to some API functions. A callback function allows your program to build its own routines to handle events generated by the API functions themselves. Your program must contain any callback functions it wishes to use, because Windows does not define any "default" callback functions.
Callback functions are used when it is necessary that the program calling the API function handle some of the work. The most common examples of callback functions occur in enumeration. During an enumeration, the invoked API function locates all objects which fit the desired category. For example, when using EnumWindows to enumerate all windows currently open, the API function obtains a handle to each window it finds. However, the API function does not know what to do with all the handles it finds. This is where the callback function comes in.
Callback functions typically process some sort of data during the middle of a call to an API function. Continuing the example, EnumWindows itself will call the program-defined callback function you specify and give it the handles, one at a time, that it finds. It is up to the callback function to decide to do whatever it wants with those handles. Whenever the callback function is finished, it is given its next handle until either all windows have been enumerated or the callback function instructs EnumWindows to stop.
Of course, enumerations are only one case where callback functions might be used. They can also be used to set up a message handler routine for an object such as a window. This allows a program to define its own custom message handling routines for some messages, passing the ones it ignores to the default handler used by Windows. Even more uses for callback function are possible. Generally, an API function will require an associated callback function whenever it must leave the implementation of some necessary task to the program calling the API function.
You may recall that in an earlier page of this series, I mentioned that Visual Basic has no explicit usage of pointers. I lied. Visual Basic does in fact have a single operator -- the AddressOf operator -- which allows the programmer to access one type of pointer. The AddressOf operator is absolutely necessary in order to use a callback function. Unfortunately, since AddressOf was first introduced in Visual Basic 5.0, any earlier versions of Visual Basic have no way whatsoever of using callback functions. No other workaround exists. In fact, the AddressOf operator was developed specifically to allow Visual Basic programs to use callback functions!
The AddressOf operator has very limited uses. Most importantly, it can only return the address of a function defined by your program. This function must be Public and be defined in a module (not a form). Furthermore, the AddressOf operator itself can only be used inside of the argument list of a call to a function; it cannot be used any other time. In use, the AddressOf operator has the following syntax:
AddressOf function_name
function_name is the name of the function to get the address of. AddressOf returns a pointer to the specified function. Any callback-using API function needs a pointer to the callback function in order to invoke it. Keep in mind that the AddressOf operator itself does not call the function given to it, and it does not take the argument list of that function either. It merely returns a pointer to where that function appears in memory.
Below is a small example demonstrating how a callback function might be used. This example enumerates all of the windows currently open. For any windows which have a nonempty caption, the callback function prints that caption in the Debug window. You'll notice that the "meat" of this example is in the callback function itself. The other part of the code only serves to call the proper API function. Also note how various sections of the example must be copied into different places of a Visual Basic program.
' Display the title bar text of all top-level windows. This
' task is given to the callback function, which will receive each handle individually.
' Note that if the window has no title bar text, it will not be displayed (for clarity's sake).
' *** Place this code in a module. This is the callback function. ***
' This function displays the title bar text of the window identified by hwnd.
Public Function EnumWindowsProc (ByVal hwnd As Long, ByVal lParam As Long) As Long
Dim slength As Long, buffer As String ' title bar text length and buffer
Dim retval As Long ' return value
Static winnum As Integer ' counter keeps track of how many windows have been enumerated
winnum = winnum + 1 ' one more window enumerated....
slength = GetWindowTextLength(hwnd) + 1 ' get length of title bar text
If slength > 1 ' if return value refers to non-empty string
buffer = Space(slength) ' make room in the buffer
retval = GetWindowText(hwnd, buffer, slength) ' get title bar text
Debug.Print "Window #"; winnum; " : "; ' display number of enumerated window
Debug.Print Left(buffer, slength - 1) ' display title bar text of enumerated window
End If
EnumWindowsProc = 1 ' return value of 1 means continue enumeration
End Function
' *** Place this code wherever you want to enumerate the windows. ***
Dim retval As Long ' return value
' Use the above callback function to list all of the enumerated windows. Note that lParam is
' set to 0 because we don't need to pass any additional information to the function.
retval = EnumWindows(AddressOf EnumWindowsProc, 0)
The AddressOf operator is only valid when it appears in the argument list of a function when it is called. However, sometimes it is necessary to set a data member of a structure to the address of a function. Some API functions include a pointer to a callback function as part of the structure passed to them. However, something like the following will not work properly:
Dim ofn As OPENFILENAME
ofn.lpfnHook = AddressOf AnyFunctionName
Although you need to set ofn.lpfnHook to a pointer to the function AnyFunctionName, the AddressOf operator will cause an error. So how do you get the information into the structure's data member? Remember that AddressOf is valid only inside of a function call -- a function call in general, not necessarily a call to an API function! The trick is to create some sort of "dummy" function such as the one below.
Public Function DummyFunc (ByVal pointer As Long) As Long
DummyFunc = pointer
End Function
This function simply returns whatever value was passed to it originally. So what is the purpose of this function? Take a look at the code below, which is a modified form of the original attempt to use AddressOf with a structure:
Dim ofn As OPENFILENAME
ofn.lpfnHook = DummyFunc(AddressOf AnyFunctionName)
This code executes perfectly! AddressOf here is valid because it appears in the argument list to DummyFunc. Since DummyFunc just returns whatever was passed to it, the end result is that ofn.lpfnHook is effectively set to Addressof AnyFunctionName. The dummy function acts as a tool to work around AddressOf's limitations. You'll need to use DummyFunc or a similarly defined function whenever you want to use AddressOf outside of a function call.
<< Back to Part 4 | Contents of Introduction | Forward to Part 6 >>
Go back to the Articles section index.
Go back to the Windows API Guide home page.
Last Modified: January 17, 2000
This page is copyright © 2000 Paul Kuliniewicz.
Copyright Information Revised October 29, 2000
Go back to the Windows API Guide home page.
E-mail: vbapi@vbapi.com Send Encrypted E-Mail
This page is at http://www.vb-world.net/articles/intro/part05.html