How To Enumerate Windows Using the WIN32 API

Summary

You can list Windows, including Child Windows, using the GetWindow API. However, an application that calls GetWindow to perform this task risks being caught in an infinite loop or referencing a handle to a window that has been destroyed. Using EnumWindows for top-level Windows and EnumChildWindows for Child Windows or EnumThreadWindows for all non-child windows associated with a thread is a preferred method and is demonstrated in this article.

More Information

EnumWindows enumerates all top-level Windows, but not Child Windows. The EnumChildWindows function does not enumerate top-level windows owned by the specified window nor does it enumerate any other owned windows. The EnumThreadWindows function enumerates all non-child windows associated with a thread. This sample uses all three to list the Class and Title of Windows it finds. Note that while all Windows have a Class, not all have a Title.

Step-by-Step Example

  1. Start a new project in Visual Basic. Form1 is created by default.
  2. Add two CommandButtons and a TextBox.
  3. Copy the following code into the Form's module:
          Option Explicit

    Private Sub Command1_Click()
    Dim lRet As Long, lParam As Long
    Dim lhWnd As Long

    lhWnd = Me.hwnd ' Find the Form's Child Windows
    ' Comment the line above and uncomment the line below to
    ' enumerate Windows for the DeskTop rather than for the Form
    'lhWnd = GetDesktopWindow() ' Find the Desktop's Child Windows
    ' enumerate the list
    lRet = EnumChildWindows(lhWnd, AddressOf EnumChildProc, lParam)
    End Sub

    Private Sub Command2_Click()
    Dim lRet As Long
    Dim lParam As Long

    'enumerate the list
    lRet = EnumWindows(AddressOf EnumWinProc, lParam)
    ' How many Windows did we find?
    Debug.Print TopCount; " Total Top level Windows"
    Debug.Print ChildCount; " Total Child Windows"
    Debug.Print ThreadCount; " Total Thread Windows"
    Debug.Print "For a grand total of "; TopCount + _
    ChildCount + ThreadCount; " Windows!"
    End Sub
  4. Add a Module with the following code:
          Option Explicit

    Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
    End Type

    Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, _
    lpRect As RECT) As Long

    Declare Function MoveWindow Lib "user32" (ByVal hwnd As Long, _
    ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, _
    ByVal nHeight As Long, ByVal bRepaint As Long) As Long

    Declare Function GetDesktopWindow Lib "user32" () As Long

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

    Declare Function EnumChildWindows Lib "user32" (ByVal hWndParent _
    As Long, ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long

    Declare Function EnumThreadWindows Lib "user32" (ByVal dwThreadId _
    As Long, ByVal lpfn As Long, ByVal lParam As Long) As Long

    Declare Function GetWindowThreadProcessId Lib "user32" _
    (ByVal hwnd As Long, lpdwProcessId As Long) As Long

    Declare Function GetClassName Lib "user32" Alias "GetClassNameA" _
    (ByVal hwnd As Long, ByVal lpClassName As String, _
    ByVal nMaxCount As Long) As Long

    Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" _
    (ByVal hwnd As Long, ByVal lpString As String, _
    ByVal cch As Long) As Long

    Public TopCount As Integer ' Number of Top level Windows
    Public ChildCount As Integer ' Number of Child Windows
    Public ThreadCount As Integer ' Number of Thread Windows

    Function EnumWinProc(ByVal lhWnd As Long, ByVal lParam As Long) _
    As Long
    Dim RetVal As Long, ProcessID As Long, ThreadID As Long
    Dim WinClassBuf As String * 255, WinTitleBuf As String * 255
    Dim WinClass As String, WinTitle As String

    RetVal = GetClassName(lhWnd, WinClassBuf, 255)
    WinClass = StripNulls(WinClassBuf) ' remove extra Nulls & spaces
    RetVal = GetWindowText(lhWnd, WinTitleBuf, 255)
    WinTitle = StripNulls(WinTitleBuf)
    TopCount = TopCount + 1
    ' see the Windows Class and Title for each top level Window
    Debug.Print "Top level Class = "; WinClass; ", Title = "; WinTitle
    ' Usually either enumerate Child or Thread Windows, not both.
    ' In this example, EnumThreadWindows may produce a very long list!
    RetVal = EnumChildWindows(lhWnd, AddressOf EnumChildProc, lParam)
    ThreadID = GetWindowThreadProcessId(lhWnd, ProcessID)
    RetVal = EnumThreadWindows(ThreadID, AddressOf EnumThreadProc, _
    lParam)
    EnumWinProc = True
    End Function

    Function EnumChildProc(ByVal lhWnd As Long, ByVal lParam As Long) _
    As Long
    Dim RetVal As Long
    Dim WinClassBuf As String * 255, WinTitleBuf As String * 255
    Dim WinClass As String, WinTitle As String
    Dim WinRect As RECT
    Dim WinWidth As Long, WinHeight As Long

    RetVal = GetClassName(lhWnd, WinClassBuf, 255)
    WinClass = StripNulls(WinClassBuf) ' remove extra Nulls & spaces
    RetVal = GetWindowText(lhWnd, WinTitleBuf, 255)
    WinTitle = StripNulls(WinTitleBuf)
    ChildCount = ChildCount + 1
    ' see the Windows Class and Title for each Child Window enumerated
    Debug.Print " Child Class = "; WinClass; ", Title = "; WinTitle
    ' You can find any type of Window by searching for its WinClass
    If WinClass = "ThunderTextBox" Then ' TextBox Window
    RetVal = GetWindowRect(lhWnd, WinRect) ' get current size
    WinWidth = WinRect.Right - WinRect.Left ' keep current width
    WinHeight = (WinRect.Bottom - WinRect.Top) * 2 ' double height
    RetVal = MoveWindow(lhWnd, 0, 0, WinWidth, WinHeight, True)
    EnumChildProc = False
    Else
    EnumChildProc = True
    End If
    End Function

    Function EnumThreadProc(ByVal lhWnd As Long, ByVal lParam As Long) _
    As Long
    Dim RetVal As Long
    Dim WinClassBuf As String * 255, WinTitleBuf As String * 255
    Dim WinClass As String, WinTitle As String

    RetVal = GetClassName(lhWnd, WinClassBuf, 255)
    WinClass = StripNulls(WinClassBuf) ' remove extra Nulls & spaces
    RetVal = GetWindowText(lhWnd, WinTitleBuf, 255)
    WinTitle = StripNulls(WinTitleBuf)
    ThreadCount = ThreadCount + 1
    ' see the Windows Class and Title for top level Window
    Debug.Print "Thread Window Class = "; WinClass; ", Title = "; _
    WinTitle
    EnumThreadProc = True
    End Function

    Public Function StripNulls(OriginalStr As String) As String
    ' This removes the extra Nulls so String comparisons will work
    If (InStr(OriginalStr, Chr(0)) > 0) Then
    OriginalStr = Left(OriginalStr, InStr(OriginalStr, Chr(0)) - 1)
    End If
    StripNulls = OriginalStr
    End Function

  5. Run the project and click on Command1. This enumerates the Form's child Windows until it finds the TextBox, then doubles the height of the TextBox and moves it to position 0, 0 (upper-left of the Form.)
  6. Click on Command2. It now lists the Class and Title (if there is one) for all top-level Windows, for their Child Windows, and any non-child Windows associated with their threads, to the Immediate Window. Note that this can be a very long list and may take a minute to complete.
  7. From the Run Menu, choose End, or click the END button to stop the program. Change the code in Sub Command1_Click according to the comments to pass the handle from GetDesktopWindow. Run the project and click on Command1 to enumerate all child Windows of the DeskTop. Note that this procedure can take some time to complete.

References

For more information, please search on the following topics in either the Win32 Programmer's Reference or the Microsoft Developer Network (MSDN) Library CD-ROM:


  • EnumWindows
  • EnumChildWindows
  • EnumThreadWindows
  • GetDesktopWindow
  • GetClassName
  • GetWindowRect
  • GetWindowText
  • GetWindowThreadProcessId
  • MoveWindow
Propiedades

Id. de artículo: 183009 - Última revisión: 07/01/2004 - Revisión: 1

Comentarios