Microsoft Office 2000/Visual Basic Programmer's Guide   

Creating a Callback Function

You can determine whether a DLL function requires a callback function by looking at its arguments and reading the documentation for the function. An argument that takes a pointer to a callback function is a long pointer, usually prefaced with the prefix "lp". Also, the name of the argument usually ends in "Func", indicating that it takes a pointer to a function. For example, take a look at the Declare statement for the EnumWindows function. The lpEnumFunc argument indicates that the function requires a callback function.

Declare Function EnumWindows Lib "user32" (ByVal lpEnumFunc As Long, _
              ByVal lngParam As Long) As Long

So how do you know what the callback function should look like? The answer is in the documentation for the function. Each callback function has different arguments. Unfortunately, although information about DLL functions requiring callbacks is available in Win32API.txt, information about creating the callback functions themselves is not.

The Microsoft Platform SDK, available on the Microsoft Developer Network site at http://msdn.microsoft.com/developer/, contains information about all DLL functions that require callbacks and their corresponding callback functions, and is probably the best source for this information. However, in order to use it, you need to be able to interpret C/C++ documentation and translate it to VBA.

You can name the callback function whatever you want to; the suggested name is usually the name of the DLL function, followed by "Proc". For example, the suggested name for the callback function of the EnumWindows function is EnumWindowsProc. Note that you can have more than one callback function for the same DLL function.

Here's the C/C++ definition for the EnumWindowsProc function, as described in the Platform SDK:

BOOL CALLBACK EnumWindowsProc( 
   HWND hwnd, // handle to parent window 
   LPARAM lParam // application-defined value
);

Translated to VBA, this function becomes:

Function EnumWindowsProc(ByVal hwnd As Long, ByVal lParam As Long) As Long
End Function

The hwnd argument is a handle to a window. The lParam argument is an application-defined argument that can be any data type. The value that you pass to the lParam argument for the EnumWindows function is also the value passed to the lParam argument of the EnumWindowsProc function. You don't have to define lParam as a Long; you can define it as another type if you know that you will be passing in, say, a ListBox control, or an array. Also, you don't have to pass any value to lParam at all.

Callback functions usually return a Long value — nonzero for success, zero for failure. To continue enumerating, a callback function must return True, so you must explicitly set the return value to True within the function. Setting it to False halts the enumeration. The function will run until it either finishes enumerating or returns False.

Important   Be careful when defining the callback function. You must include the ByVal keyword where necessary, and you must include the return value for the function. Also, the function must be declared exactly according to the documentation. If you don't define the function correctly, your solution will most likely cause an application error when you try to run it.

When you call the EnumWindows function, this function calls the EnumWindowsProc function in your VBA project for each existing window, passing in the window's handle and whatever value is contained in the lParam argument. You can add whatever code you want within the callback function.

The following example shows a callback function for the EnumWindows function. This callback function, EnumWindowsProc, adds each visible parent window to the ParentWindows collection, a custom collection of ParentWindow objects. In order to add windows to the collection, the collection object is passed into the callback function in the lParam argument. The procedure calls two other functions: the IsWindowVisible API function, which determines whether a given window is visible, and the GetWindowCaption procedure, which in turn calls the GetWindowText API function to return the window's caption.

Note   Parent windows are primary application windows; child windows are windows that exist within parent windows. A separate DLL function, EnumChildWindows, exists for enumerating child windows within parent windows. The EnumWindows.xls sample file demonstrates both functions.

Function EnumWindowsProc(ByVal hWnd As Long, ByVal lParam As Object) As Long
   ' Add all visible windows to ParentWindows collection.
   ' This is the callback function called by EnumWindows.
   '
   ' The hWnd argument provides a handle to a specific window each time
   ' the callback function runs.
   ' The lParam argument can take any data that the user wants to pass to
   ' the function. In this case, it is defined as type Object, so that
   ' the EnumWindows function can pass in a reference to the ParentWindows
   ' collection.
   '
   ' You could use this function to return both visible and non-visible
   ' windows by removing the code that calls the IsWindowVisible function.
   
   Dim strCaption As String
   Dim pwnNew As ParentWindow
   
   ' Check whether this window is visible.
   If IsWindowVisible(hWnd) Then
      ' Add new ParentWindow object to ParentWindows collection.
      Set pwnNew = lParam.Add(hWnd)
      ' Call function to enumerate child windows.
      EnumChildWindows hWnd, AddressOf EnumChildWindowsProc, _
         pwnNew.ChildWindows
   End If
  
   ' Return True to continue enumerating windows.
   ' Function will stop running when all windows have been
   ' enumerated.
   EnumWindowsProc = True
End Function

The EnumWindowsProc callback function is available in the modEnumWindows module in EnumWindows.xls in the ODETools\V9\Samples\OPG\Samples\CH10 subfolder on the Office 2000 Developer CD-ROM. This procedure is used to create objects in a small custom object model. The ParentWindows collection contains ParentWindow objects, which refer to individual visible windows. The address of the EnumWindowsProc function is passed to the EnumWindows function, which is called from the Class_Initialize event procedure for the ParentWindows collection, so that the existing windows are immediately added to the collection.

Once the ParentWindows collection has been initialized, you can refer to a ParentWindow object within the collection by its index or by its key, which is the window's handle. Use the Item property of the ParentWindows collection to refer to a member of the collection. For example, the following code fragment prints the caption of the third ParentWindow object in the ParentWindows collection:

Debug.Print ParentWindows.Item(3).Caption