|[ Team LiB ]|
Recipe 11.9 Retrieve a List of All Top-Level Windows
You know you can determine if specific applications are currently running (as shown in the Solution in Recipe 11.8), but now you'd like to obtain a list of all the running applications. That way, you could decide, as part of your application, what to present to your users. Is there a way to walk through all the open main windows and build up a list?
Windows includes API functions that allow you to walk down and around the tree of open windows, starting with the main desktop window. This solution provides a function that will do that for you, filling an array with information on each top-level window. You can then use that array to list applications, switch to them, or close them (see the Solution in Recipe 11.10 for information on closing other windows).
Load and run frmListWindows from 11-09.MDB. This sample form fills a list box with all the top-level windows and provides a button that uses the VBA AppActivate command to display the selected window. In addition, the "Show visible windows only" checkbox allows you to add invisible windows to the list. Of course, attempting to use AppActivate to switch to an invisible window will fail. Figure 11-11 shows the sample form in action.
To include this functionality in your own applications, follow these steps:
This example uses several functions for navigating through the hierarchy of windows. Table 11-11 describes the functions.
The acbWindowList function first retrieves a handle to the main desktop window, using GetDesktopHWnd. Once it knows that, it can find the handle for the desktop's first child window, using GetWindow. From then on, as long as the handle for the current window isn't 0, the code loops, filling in the array with information about the current window and then moving on to the next window with the GetWindow function. You'll note that the loop skips windows without captions (of which there are quite a few). Windows maintains a number of top-level hidden windows without captions for its own use. In addition, by specifying the blnVisibleOnly parameter for acbWindowList, you can include or exclude invisible windows. Windows sets up a number of invisible windows, and you probably won't want them to show up in your list. If you're interested, however, pass in False for this parameter to add all the hidden windows to your list. The code for the acbWindowList function is as follows:
Type acb_tagWindowInfo strCaption As String hWnd As Long strClass As String End Type Public Function acbWindowList(aWI( ) As acb_tagWindowInfo, _ ByVal blnVisibleOnly As Boolean) As Integer ' Fill an array with a list of all the currently ' open top-level windows. Dim hWnd As Long Dim strCaption As String Dim intCount As Integer Dim lngStyle As Long ' Get the desktop window and, from there, the first ' top-level window. hWnd = acb_apiGetDesktopWindow( ) hWnd = acb_apiGetWindow(hWnd, GW_CHILD) ' Loop through all the top-level windows. Do While hWnd <> 0 strCaption = acbGetCaption(hWnd) If Len(strCaption) > 0 Then ' If you got a caption, add one element to the output ' array, and fill in the information (name and hWnd). lngStyle = acb_apiGetWindowLong(hWnd, GWL_STYLE) ' The Imp operator (Implies) returns True unless ' the first condition is True and the second is False, ' so this condition will be true unless you're ' showing visible only and the window is not visible. If blnVisibleOnly Imp (WS_VISIBLE And lngStyle) Then ReDim Preserve aWI(0 To intCount) aWI(intCount).strCaption = strCaption aWI(intCount).hWnd = hWnd aWI(intCount).strClass = CalcClassName(hWnd) intCount = intCount + 1 End If End If ' Move to the next top-level window. hWnd = acb_apiGetWindow(hWnd, GW_HWNDNEXT) Loop ' Return the number of windows. acbWindowList = intCount End Function
You may find it instructive to study the code in the sample form's module. It calls acbWindowList and then uses a list-filling callback function to fill the list box on the form with window captions, classes, and handles. This is a perfect example of when you'd use such a function: you need to fill a control with data from an array that can't be gathered until the application is running, and the array might be too large to fit within the character limit imposed when you call the control's AddItem method.
Some of the windows on the list exist at the time the form is filling its list, but are not available (the Access Immediate window, for example). You can attempt to switch to them, but the attempt will fail. The code attached to the checkmark button's Click event disregards errors, so it just keeps going if an error occurs when it tries to switch the active window. See the Solution in Recipe 11.10 for information on deleting windows in this list.
|[ Team LiB ]|