I have been using GetWindow recursively for about 6 - 7 years now, to search
for a particular Window. We've never had a problem up until now.
Recently, a customer started reporting application hangs. We traced this
down into our code to the point just before the first (outer) call into the
recursive function.
A little Googling turns up the following:
--------------------
http://support.microsoft.com/kb/183009
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...
--------------------
There are plenty of hits on GetWindow and 'infinite loop', however every
page is simply parroting the same little blurb. I've yet to see any
explanation as to what precise circumstance will trigger an infinite loop
when using GetWindow. The only circumstance I could see would be in the
event that a call to GetWindow starting with a particular hWnd would return
the same hWnd. Would this even be possible? Can a Window be both its own
parent and child? Or can a circular parent child relationship exist between
two or more Windows?
Does any one have any additional information to augment Microsoft's advice
that an application that calls GetWindow to perform this task risks being
caught in an infinite loop? Does Microsoft simply mean that recursion is
inherently risky? Or is there something specific to GetWindow which will
create a particular condition which will cause a recursive algorithm to
enter an endless loop?
I've posted my function below. As you can see, the loop for siblings ends
when a 0 is returned. This should take care for normal end-of-siblings or
for any error encountered, since the defined functional return in an error
condition is 0. So my own loop doesn't seem succeptible to inifinite
looping. And I don't see how the recursion itself can be infinite unless
it's possible for a circular parent child relationship within the Windows
hierarchy. Is such a thing possible?
Thanks for any advice which you can provide.
- Joseph Geretz -
Public Function FindWindowLike(hWnds() As Long, _
WinTitles() As String, _
WinClasses() As String, _
Optional ByVal hWndStart As Long = 0, _
Optional ByVal WinTitle As String = "", _
Optional ByVal WinClass As String = "", _
Optional ByVal WinChildID As Long = 0, _
Optional ByVal CaseSensitive As Boolean =
True) _
As Long
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' This function calls itself recursively. Static variables '
' keep track of the level of recursion. '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Hold the RecLevel of recursion:
Static RecLevel As Long
' Hold the number of matching windows:
Static NumMatches As Long
Dim hWnd As Long
Dim APIReturn As Long
Dim bMatch As Boolean
Dim sWinTitle As String
Dim sWinClass As String
Dim lID As Long
' Initialize if necessary:
If RecLevel = 0 Then
NumMatches = 0
ReDim hWnds(0 To 0)
If hWndStart < 1 Then
hWndStart = GetDesktopWindow()
End If
End If
If CaseSensitive = False Then
WinTitle = UCase$(WinTitle)
WinClass = UCase$(WinClass)
End If
' Increase recursion counter:
RecLevel = RecLevel + 1
' Priming action; Get first child window.
hWnd = GetWindow(hWndStart, GW_CHILD)
Do Until hWnd = 0
' Search children by recursion:
APIReturn = FindWindowLike(hWnds(), _
WinTitles, _
WinClasses, _
hWnd, _
WinTitle, _
WinClass, _
WinChildID, _
CaseSensitive)
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' OK, let's set the match flag to True. Then, as long '
' as the match flag remains True, we'll apply each '
' filter criteria in sequence. After applying all filters, '
' if the match flag is still True, then we've got a match! '
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
bMatch = True
If WinTitle <> "" Then
sWinTitle = GetWinTitle(hWnd)
If CaseSensitive = False Then
sWinTitle = UCase$(sWinTitle)
End If
If Not (sWinTitle Like WinTitle) Then
bMatch = False
End If
End If
If bMatch = True Then
If WinClass <> "" Then
sWinClass = GetWinClass(hWnd)
If CaseSensitive = False Then
sWinClass = UCase$(sWinClass)
End If
If Not (sWinClass Like WinClass) Then
bMatch = False
End If
End If
End If
If bMatch = True Then
If WinChildID > 0 Then
If GetParent(hWnd) <> 0 Then
lID = GetWindowLW(hWnd, GWL_ID)
If WinChildID <> lID Then
bMatch = False
End If
Else
bMatch = False
End If
End If
End If
If bMatch = True Then
' If find a match, increment counter and
' add handle to array:
NumMatches = NumMatches + 1
ReDim Preserve hWnds(0 To NumMatches)
ReDim Preserve WinTitles(0 To NumMatches)
ReDim Preserve WinClasses(0 To NumMatches)
hWnds(NumMatches) = hWnd
WinTitles(NumMatches) = GetWinTitle(hWnd)
WinClasses(NumMatches) = GetWinClass(hWnd)
End If
' Get next child window:
hWnd = GetWindow(hWnd, GW_HWNDNEXT)
Loop
' Decrement recursion counter
RecLevel = RecLevel - 1
' Return the number of windows found:
FindWindowLike = NumMatches
End Function