Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
Home
Discussion GroupsVB SyntaxEnterprise DevelopmentDatabase AccessControlsCOMWin APICrystal ReportDeploymentGeneralGeneral 2
Related Topics
VB.NET / ASP.NETMS SQL ServerMS AccessOther Database ProductsMore Topics ...

VB Forum / Win API / March 2008



Tip: Looking for answers? Try searching our database.

GetWindow - Infinite Loop?

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Joseph Geretz - 11 Mar 2008 00:24 GMT
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
Thorsten Albers - 11 Mar 2008 01:45 GMT
Joseph Geretz <jgeretz@nospam.com> schrieb im Beitrag
<OPi7jWwgIHA.5900@TK2MSFTNGP02.phx.gbl>...
> 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?

There are functions which may change the relationship between windows (e.g.
SetParent()) or the Z-order. Windows may be created or destroyed by
applications at any time. This IMHO may confuse the relationship between
windows and therefore cause GetWindow() to hang in an infinite loop.
Have a look at MSDN articles such as "Window Owners and Parents" , "Win32
Window Hierarchy and Styles", and "Owner-Owned Windows" (all included in
the MSDN version shipped with VB 6.0).

Signature

----------------------------------------------------------------------
THORSTEN ALBERS                       Universität Freiburg
                                               albers@
                                                      uni-freiburg.de
----------------------------------------------------------------------

mayayana - 11 Mar 2008 04:31 GMT
I've read the same blurb. And EnumChildWindows
will search through all levels of children with
a convenient callback function. So why not just
switch to using that?

> 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.
[quoted text clipped - 168 lines]
>
> End Function
Joseph Geretz - 11 Mar 2008 23:14 GMT
> So why not just
> switch to using that?

You're in management, right? ;-)

Seriously, *just switching* cost the better part of the afternoon, and
significantly rearchitects the solution.. (Is the common BAS module now
possibly subject to reentrancy problems?) Anyway, I've gone ahead and done
this because it does seem, from all our diagnostics, as though the GetWindow
function has reached the limits of its usefulness. The revised code works
initially which is nice, but obviously, the entire application needs serious
regression testing because Window resolution is at the heart of our most
core application features.

Ce la vie.

Thanks for your suggestion!

- Joseph Geretz -

>  I've read the same blurb. And EnumChildWindows
> will search through all levels of children with
[quoted text clipped - 179 lines]
>>
>> End Function
mayayana - 12 Mar 2008 04:11 GMT
> You're in management, right? ;-)

> Seriously, *just switching* cost the better part of the afternoon, and
> significantly rearchitects the solution..

"Rearchitects the solution"... So you would be in the
marketing dept., then?  :)

> (Is the common BAS module now
> possibly subject to reentrancy problems?)

  I can't imagine where you'd need to call it
concurrently, but I suppose it could be put into
a class if you did need to. I've typically used it in a
situation where I've got two module-scope variables -
a string and a long. The long gets set to 0 and the string
gets set to, say, a class name, before the window-hunting
function is called. With each call to the EnumChildProc
I'm then checking the class name of the window
against the string variable. If I find my match I then
set the long variable to that window's hWnd and return
0 from EnumChildProc to end the iteration. So when
EnumChildWindows returns I've either got 0 or an hWnd
in the module-scope long. (You probably already know all
that by now.)

   I think I was originally using GetWindow, checking
class name with a Select Case in order to step
down through a known window hierarchy. But then
I noticed the same warning you had seen, and came
across a posting by Raymond Chen noting that
EnumChildWindows will follow all children down until
they've all been returned. So that method turned
out to be a lot easier. (I'm taking the doc's word for
it that EnumChildWindows has its own built-in
stability.)
Joseph Geretz - 12 Mar 2008 06:40 GMT
>   I can't imagine where you'd need to call it
> concurrently, but I suppose it could be put into
> a class if you did need to.

That's news to me. You can put an API callback handler into a Class? I
thought this has to be in a BAS module?

My solution differs from yours slightly in that I do not stop after the
first match. My application searches for all qualifying windows and returns
an array of qualifying hWnds. So I need to cycle through to the end in any
case, building up an array as each qualifying window is found.

The existing function takes an array (actually 3 arrays) passed in as
parametes. On the initial call (recursion level = 0) the arrays are redimmed
to 0 and the recursive search begins. By the time the search unwinds, the
array parameters contain hwnds and info for all qualifying entries.

The recoding was a bit of a pain since I wanted to remain compatible with
existing clients. The arrays are passed in on the function call, and now
these arrays, along with a plethora of search parameters are passed into the
BAS module. The callback function now performs the array accumulation. When
the Enum call finishes, the arrays must be scooped out of the BAS module and
returned back as the parameters to the original call. Not a huge deal, but a
significant number of lines of code were moved around.

Anyway, as soon as this passes QA, I'm going to apply for a transfer to
marketing on your recommendation - I could use some relaxation!

:-)

- Joe Geretz -

>> You're in management, right? ;-)
>
[quoted text clipped - 32 lines]
> it that EnumChildWindows has its own built-in
> stability.)
mayayana - 12 Mar 2008 15:18 GMT
> That's news to me. You can put an API callback handler into a Class? I
> thought this has to be in a BAS module?

  Woops. AddressOf doesn't work inside a class? I would
have thought that sending the function pointer could be
done from anywhere, but maybe inside a class it's a relative
pointer, like a vTable. (?) I guess I've never had an occasion
to try it, so I don't know.
Karl E. Peterson - 12 Mar 2008 17:34 GMT
>> That's news to me. You can put an API callback handler into a Class? I
>> thought this has to be in a BAS module?
[quoted text clipped - 4 lines]
> pointer, like a vTable. (?) I guess I've never had an occasion
> to try it, so I don't know.

Nope, not directly.  With callbacks that let you pass a DWORD back to yourself,
there's an easy workaround, though.  You just pass ObjPtr(Me), and the callback
procedure is then enabled to directly recieve a reference.  Something like this
example taken from http://vb.mvps.org/samples/TimerObj . . .

In the class:

  ' Pass pointer to Me so we can return event to this instance.
  m_TmrID = SetTimer(m_hWnd, ObjPtr(Me), m_Interval, AddressOf TimerProc)

In the BAS module:

  Public Sub TimerProc(ByVal hWnd As Long, _
                       ByVal uMsg As Long, _
                       ByVal oTimer As CTimer, _
                       ByVal dwTime As Long)
     ' Alert appropriate timer object instance.
     oTimer.RaiseTimer
  End Sub

Same strategy would work with most Enum* calls.
Signature

.NET: It's About Trust!
http://vfred.mvps.org

mayayana - 12 Mar 2008 19:52 GMT
Thanks. I never thought about that. Matthew
Curland showed a similar method for UserControls
to subclass themselves, but it hadn't occurred
to me that calling out to the .bas might be
required.

> Nope, not directly.  With callbacks that let you pass a DWORD back to yourself,
> there's an easy workaround, though.  You just pass ObjPtr(Me), and the callback
[quoted text clipped - 20 lines]
> .NET: It's About Trust!
>  http://vfred.mvps.org
Karl E. Peterson - 12 Mar 2008 20:02 GMT
>  Thanks. I never thought about that. Matthew
> Curland showed a similar method for UserControls
> to subclass themselves, but it hadn't occurred
> to me that calling out to the .bas might be
> required.

I'd be interested in looking at that method by Matt.  Do you recall, was it in his
book or one of his VBPJ columns?

I'm not honestly sure why AddressOf (doesn't) work the way it does.  Sorta smells of
paternalistic protection from ourselves, doesn't it?  I suspect they figured it was
"safe" with BAS modules as the code pointer would be valid for the life of the app?

Thanks...   Karl

>> Nope, not directly.  With callbacks that let you pass a DWORD back to yourself,
>> there's an easy workaround, though.  You just pass ObjPtr(Me), and the callback
[quoted text clipped - 20 lines]
>> .NET: It's About Trust!
>>  http://vfred.mvps.org

Signature

.NET: It's About Trust!
http://vfred.mvps.org

Jim Mack - 12 Mar 2008 20:20 GMT
> I'm not honestly sure why AddressOf (doesn't) work the way it does.
> Sorta smells of
> paternalistic protection from ourselves, doesn't it?  I suspect
> they figured it was
> "safe" with BAS modules as the code pointer would be valid for the
> life of the app?

I suspect it has to do with the fact that modules are static. Is the
address of a function in a class distinct across all instances? What
happens when it goes out of scope or is otherwise disposed of?

Of course there are ways around anything, but this smells more like
"too hard" than "paternalistic". (-:

Signature

       Jim

Karl E. Peterson - 12 Mar 2008 20:56 GMT
>> I'm not honestly sure why AddressOf (doesn't) work the way it does.
>> Sorta smells of
[quoted text clipped - 6 lines]
> address of a function in a class distinct across all instances? What
> happens when it goes out of scope or is otherwise disposed of?

Exactly.  I would think, though, that the address of a function in a class
*instance* would be static, wouldn't you?  I suppose class instances may be juggled
around in memory?

> Of course there are ways around anything, but this smells more like
> "too hard" than "paternalistic". (-:

I have a vague recollection of Matt Curland telling me otherwise.  Could be I'm
mistaken, and we were talking about something much easier, like inheritance.  Just
don't recall precisely. <g>
Signature

.NET: It's About Trust!
http://vfred.mvps.org

Jim Mack - 12 Mar 2008 22:19 GMT
>>> I'm not honestly sure why AddressOf (doesn't) work the way it
>>> does. Sorta smells of
[quoted text clipped - 11 lines]
> a class *instance* would be static, wouldn't you?  I suppose class
> instances may be juggled around in memory?

I think not, but to expose a function like that would break
encapsulation. Anything not published in the interface should not be
accessible (by theory). Again, that's not to say you couldn't find a
way, but it would be a hack.

Having said that, if you exposed the function of interest as a method,
its address would be in the vtable, which is at a known (or
computable) offset from the ObjPtr. So, if you really needed to, I
guess you could.

Signature

       Jim

Karl E. Peterson - 12 Mar 2008 22:39 GMT
>>>> I'm not honestly sure why AddressOf (doesn't) work the way it
>>>> does. Sorta smells of
[quoted text clipped - 21 lines]
> computable) offset from the ObjPtr. So, if you really needed to, I
> guess you could.

I was definitely thinking you'd have to point to a public method of the object, of
course.  I guess the attempts I've seen at this are all based around that vtable
notion, yeah.  Calling back into the object is a PITA, but doable.
Signature

.NET: It's About Trust!
http://vfred.mvps.org

Michael C - 19 Mar 2008 06:06 GMT
> Exactly.  I would think, though, that the address of a function in a class
> *instance* would be static, wouldn't you?

I know you'll know some or most of this but will explain it all for
everyone's benefit.

There could be 1 million instance of a class but there is only 1 function
shared by the 1 million instances that is always in the same location and
can be called via a callback. The function will never move in memory and
will not disappear if all the instances of a class are destroyed. I presume
the function exists before any classes are created. The real reason that
AddressOf doesn't work on class function is because it has 2 extra hidden
parameters, the ObjPtr param and the return value which is returned as a
byref param. The actual return value is the hResult which indicates to the
caller that an error occurred. As an example, a function in a class like
this:

public Function AddOne(ByVal Value As long) as Long

will actually look like this

public Function AddOne(ByVal ObjPointer As Long, ByVal Value as long, ByRef
ReturnValue as Long) as Long

The ObjPointer parameter simply indicates to the function what instance of
the class should be used to reference data. The reasons this can't be called
using AddressOf are fairly obvious but it would be possible for MS to
provide a proxy calling function but they didn't implement this.

The other interesting thing is the function wouldn't have to be private as
the private functions are in the vtable also, they're just at the end
(although this might only apply to PCode from what I remember).

> I have a vague recollection of Matt Curland telling me otherwise.  Could
> be I'm mistaken, and we were talking about something much easier, like
> inheritance.  Just don't recall precisely. <g>

No, Matt got around it by providing a proxy function in assembly code.

Michael
Michael C - 19 Mar 2008 06:08 GMT
>  I suppose class instances may be juggled around in memory?

Accidentally pushed ctrl-enter before answering this one. AFAIK, class
instance are not moved around in memory. Anyone who used ObjPtr(Me) would be
in a lot of trouble if they did :-)

Michael

>>> I'm not honestly sure why AddressOf (doesn't) work the way it does.
>>> Sorta smells of
[quoted text clipped - 17 lines]
> be I'm mistaken, and we were talking about something much easier, like
> inheritance.  Just don't recall precisely. <g>
Scott Seligman - 12 Mar 2008 21:47 GMT
>>  Thanks. I never thought about that. Matthew
>> Curland showed a similar method for UserControls
[quoted text clipped - 9 lines]
>suspect they figured it was "safe" with BAS modules as the code pointer
>would be valid for the life of the app?

If AddressOf returned something for a class's method, how would anyone
use it? There's only one function pointer for each bit of code in a
function. If you were to call into a pointer returned by AddressOf, the
code in your class would have no way to access "Me". When you call a
function, VB has to pass the reference to the class, I'm sure you could
come up with limited uses for AddressOf in a class, but for the general
case of using it for a call back into some standard Windows API, it'd be
useless.

Other languages have similar issues with passing pointers to functions
in classes. The answer is always the same, either have a static
function, or a function outside of the class, with enough intelligence
to know how to call back into the object from some bit of data either
passed into the callback, or maintained somewhere else.

Signature

--------- Scott Seligman <scott at <firstname> and michelle dot net> ---------
  Never underestimate the bandwidth of a station wagon full of tapes
  hurtling down the highway.
  -- Andrew S. Tanenbaum

mayayana - 13 Mar 2008 00:25 GMT
> >  Thanks. I never thought about that. Matthew
> > Curland showed a similar method for UserControls
[quoted text clipped - 4 lines]
> I'd be interested in looking at that method by Matt.  Do you recall, was it in his
> book or one of his VBPJ columns?

 It's in his book and a limited example was in
August 2001 VBPJ:
Black Belt Programming: Provide Pointers to Class Functions

 If you have the book there are sample projects
on the CD, in PowerVB\Samples\CreateWindow.

 If you have trouble finding it look for
"InitPushParamThunk". That's the name of a function
using inline assembly that's part of the very small
amount of code needed to set up any number of
UCs to subclass themselves and/or their consituent
controls. The whole thing was presented in his book
as a way to use owner-drawn windows that could
be wrapped in the convenience of VB by "anchoring"
them on a UC.

 (I seem to remember a group discussion some time back
about this, so I went to check your site. I was just looking
at your HookMe sample, trying to figure out if that was a
similar approach, but so far I don't get it. You're using classes
to enable multiple subclasses? I've never seen GetProp and
SetProp before.)
mayayana - 13 Mar 2008 15:49 GMT
Woops. Never mind all of that. While Matthew
Curland's code is interesting, and I'm glad I
found the HookMe sample, we were talking about
callbacks and I realized that I've wandered off into
my own private subclassing Idaho. :)

>   It's in his book and a limited example was in
> August 2001 VBPJ:
[quoted text clipped - 19 lines]
> to enable multiple subclasses? I've never seen GetProp and
> SetProp before.)
Karl E. Peterson - 13 Mar 2008 19:51 GMT
>  Woops. Never mind all of that. While Matthew
> Curland's code is interesting, and I'm glad I
> found the HookMe sample, we were talking about
> callbacks and I realized that I've wandered off into
> my own private subclassing Idaho. :)

Something appealing about that.  Can't say for sure what it may be.  <g>
Signature

.NET: It's About Trust!
http://vfred.mvps.org

Karl E. Peterson - 13 Mar 2008 19:51 GMT
>> >  Thanks. I never thought about that. Matthew
>> > Curland showed a similar method for UserControls
[quoted text clipped - 22 lines]
> be wrapped in the convenience of VB by "anchoring"
> them on a UC.

I'll definitely take a closer look.  Thanks.
Signature

.NET: It's About Trust!
http://vfred.mvps.org

Michael C - 19 Mar 2008 05:48 GMT
>>   I can't imagine where you'd need to call it
>> concurrently, but I suppose it could be put into
>> a class if you did need to.
>
> That's news to me. You can put an API callback handler into a Class? I
> thought this has to be in a BAS module?

http://mikesdriveway.com/programming

and look at the sample "VB6 API callbacks without addressof"

BTW, to solve your problem you could add some code in to check for an
infinite loop and log the results. This would at least tell you if the
parent was it's own child or if there was a relationship with more than 1
window.

Michael
Scott Seligman - 19 Mar 2008 06:51 GMT
>>>   I can't imagine where you'd need to call it
>>> concurrently, but I suppose it could be put into
[quoted text clipped - 6 lines]
>
>and look at the sample "VB6 API callbacks without addressof"

Looks like the code's really at
http://mikesdriveway.com/code/

Using some machine code, he's creating a stub function in memory that
calls into the class function by cracking the vtable.

A little too hackish for my tastes, but I do give him points for a
clever method to solve the problem.

Signature

--------- Scott Seligman <scott at <firstname> and michelle dot net> ---------
  It has been said that democracy is the worst form of government
  except all the others that have been tried.
  -- Sir Winston Churchill

Michael C - 19 Mar 2008 07:17 GMT
>>and look at the sample "VB6 API callbacks without addressof"
>
> Looks like the code's really at
> http://mikesdriveway.com/code/

Good pickup. I thought I changed that before posting.

> Using some machine code, he's creating a stub function in memory that
> calls into the class function by cracking the vtable.

Yes.

> A little too hackish for my tastes, but I do give him points for a
> clever method to solve the problem.

If the language did support callbacks in a class it would do something
similar, although I do agree it is pretty hacky.

Michael
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2009 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.