HOWTO: Enumerate Windows Using the WIN32 API
ID: Q183009
|
The information in this article applies to:
-
Microsoft Visual Basic Learning, Professional, and Enterprise Editions for Windows, version 6.0
-
Microsoft Visual Basic Control Creation, Learning, Professional, and Enterprise Editions for Windows, version 5.0
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
- Start a new project in Visual Basic. Form1 is created by default.
- Add two CommandButtons and a TextBox.
- 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
- 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
- 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.)
- 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.
- 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
Additional query words:
find locate Callback
Keywords : kbAPI kbSDKWin32 kbVBp kbVBp500 kbVBp600 kbGrpVB kbDSupport
Version : WINDOWS:5.0,6.0
Platform : WINDOWS
Issue type : kbhowto
|