Why is FindWindow() not 100% reliable?
Asked Answered
M

2

11

I'm using this Delphi 7 code to detect if Internet Explorer is running:

function IERunning: Boolean;
begin
  Result := FindWindow('IEFrame', NIL) > 0;
end;

This works on 99% of the systems with IE 8,9 and 10.

But there are some systems (unfortunately none of mine, but I have two beta testers which have such systems, both Win7 x64 SP1) where FindWindow() returns 0 for IEFrame, even if IE is in memory.

So I've coded an alternate method to find the window:

function IERunningEx: Boolean;
var WinHandle : HWND;
    Name: array[0..255] of Char;
begin
  Result := False; // assume no IE window is present

  WinHandle := GetTopWindow(GetDesktopWindow);

  while WinHandle <> 0 do // go thru the window list
  begin
      GetClassName(WinHandle, @Name[0], 255);
      if (CompareText(string(Name), 'IEFrame') = 0) then
      begin // IEFrame found
          Result := True;
          Exit;             
      end;
      WinHandle := GetNextWindow(WinHandle, GW_HWNDNEXT);
  end;      
end;

The alternate method works on 100% of all systems.

My question - why is FindWindow() not reliable on some of the systems?

Mamie answered 18/3, 2013 at 4:16 Comment(20)
There are some remarks on the FindWindow and GetWindowText pages on MSDN that might provide some clues.Mack
Instead of looping through the windows manually, you should use EnumWindows() instead.Tend
Remy, EnumWindows() needs a Callback function, so cannot be used in inline code. Event handling is needed in this case. And I would have to loop after EnumWindows() is called ANYWAY. I'm using EnumWindows() in other apps, and it's rather slow also. But on a side note, it still does not explain why FindWindow() does work on 99% systems, but not on 1%.Mamie
What is inline code? You can use EnumWindows anywhere you can use FindWindow.Tengdin
I'm using the IERunning function in OnPopup event of a Popup menu. I can use FindWindow() here, and also GetTopWindow() + GetNextWindow(), but EnumWindows() is not possible, as you don't get the result right away, but in the callback function, usually after a context switch (16-25 ms). And again, as a sidenote, EnumWindows() ist not the issue here, and not really related to the question. IERunningEx() in my code is the solution for the problem (works 100%), I just need to know why FindWindow() is not reliable. This is probably also interesting to many other coders.Mamie
You get the result in a callback function. So what? It is trivially easy to implement a function with the same interface as FindWindow by calling EnumWindows. Anyway, if performance is the issue, then that's a different matter. But since it's a popup window it would seem unlikely to be a problem. Anyway, this issue may be interesting to others. How can we reproduce it?Tengdin
David, you don't get the result right away! If you call EnumWindows() you code continues the execution right way. The CallBack result is delivered AFTER the function that calls EnumWindows() is completely processed. In my case I'm using the IERunning() function to hide or show a TMenuItem in the PopupMenu.OnPopup() event. Using EnumWindows() here makes no sense whatsoever, as you get the Window List AFTER the whole popupmenu is displayed. So you cannot determine the condition of IEFrame at all if you try to use EnumWindows() in an OnPopup() event.Mamie
@Mamie No. That's incorrect. A call to EnumWindows does not return until all callbacks have been called.Tengdin
But - for the third time - EnumWindows() is not the issue here because a. does not work in this case at all b. even if it worked is NOT NEEDED AT ALL, because GetTopWindow + GetNextWindow solution works and is faster too, and c. my question is about FindWindow(). As to reproduction - I've posted in my original question that the FindWindow() problem cannot be reproduced on 99% of all systems. But I've two beta testers (from about 80) where FindWindow() gives 0 on IEFrame, even if IEFrame is present as a window, because GetTopWindow + GetNextWindow dies find it. This is the whole issue!Mamie
David, then maybe it's a debugger issue, because I've tried EnumWindows() in another case in a PopUp event, and ran into a lot of timing problems. But again, GetTopWindow + GetNextWindow is WAY faster than EnumWindows, which is extremely slow (a message for every window). But my whole question is about FindWindow(), and 90% of the comments are related to EnumWindows() which is not the issue here at all. I don't look for a replacement for FindWindow(), as GetTop(Next)Window is the perfect replacement here. I'm simple at a loss why there are systems where FindWindow() does not work on IEFrame.Mamie
Give those 2 beta testers Spy++ and let them inspect the opened IE window. Maybe they're having some extension which embeds the window somehow.Deadeye
TLama, I've send them already a test project that enumerates all Top Level Windows, and IEFrame is in the list! This same test project has another button that does a FindWindow('IEFrame', NIL) and the result is 0! I've used FindWindow() one gazillion times in different projects and never run into any issues. My best guess is that they have some utility that Hooks FindWindow() and creates this bug. I'll try to find out the similarities between the running processes on their systems. I've hoped that someone else at stackoverflow has run into a similar issue before.Mamie
I find it very hard to believe that EnumWindows is really slower than GetTopWindow/GetNextWindow. Not least because you clearly don't understand how EnumWindow works. I can't imagine you could really have benchmarked it properly. Also GetNextWindow is known to be troublesome. This is documented in MSDN: The EnumChildWindows function is more reliable than calling GetWindow in a loop. I expect that the same is true for EnumWindows.Tengdin
Anyway, I'm only making these points to correct your misunderstandings concerning EnumWindows. I appreciate that you have a valid question about FindWindow. But unless we can readily reproduce it, it sounds like a hard problem to crack.Tengdin
David, I'm using EnumWindows at different places in the same project (where timing/performance is not the issue). I've benchmarked it again two minutes ago - 10 runs. Execution time was 0 to 23 ms, in 4 of the cases longer than 15ms. In The Callback function I'm adding the WinHandles to a TList.Mamie
My benchmark shows that EnumWindows enumerates all top level windows (>300 on my system) in 0.07 milliseconds. It is true that the GetNextWindow variant is quicker. It is around 4 times quicker. It's clear that performance is not your issue since you can readily afford 0.07 milliseconds whilst showing a popup menu.Tengdin
Have you tested it multiple times? Because sometimes I get an execution time of near 0ms, but sometimes up to 25 ms.Mamie
I had to get it to enumerate 20000 times in order for the time taken to be long enough to measure accurately. Please don't tell me you timed a single enumeration!Tengdin
I wrote 4 comments before that I've made 10 runs, of which 4 were above 15ms, the rest near 0.Mamie
It's hard to reliably time a single enumeration. As I said, I did 20000 enumerations. At that point I was able to get repeatable results. As a general rule, when benchmarking you need the thing you time to be at least a second in duration.Tengdin
P
3

I'm guessing that FindWindow is declared to return a WinHandle, which is a THandle, which is an Integer, which is signed. (At least, I think this was the case many years ago when I programmed in Delphi.)

If IE has a window handle with the top bit set then it will be negative so your test will return False:

Result := FindWindow('IEFrame', NIL) > 0;

Window handles don't usually have the top bit set, but I don't know that it's impossible.

Principally answered 19/3, 2013 at 0:21 Comment(6)
I'm quire sure that WinControl handles are defined by Microsoft as typedef void *HANDLE; - thus they cannot be negative. Do I miss something here? But you may be to something here. Will have to check it out.Mamie
arx, I've checked the definition of HWND in Delphi 7: HWND = type LongWord; so it cannot be negative.Mamie
@casady If that's the case then > is the same as <> but what made you choose >? It's something I see surprisingly often. If the window is not found, the function returns 0. So, the negation of =0 is <>0. I ccan't imagine what logic leads to >0.Tengdin
@casady You said you ran a test program to reproduce the problem. Did this output the window handle returned from FindWindow or just the result of comparing it to 0? And, just to be sure, is FindWindow actually declared to return an HWND in the import unit you are using?Principally
The test program did not display the handle, only class names and captions of all top level windows & the FindWindow() result. Unfortunately both beta testers have updated to Internet Explorer 10 yesterday, and the problem went away, so I'll have to wait until another user gets this problem with IE9 and an older version of my app. As to the FindWindow declaration, it's declared as function FindWindow(lpClassName, lpWindowName: PChar): HWND; , HWND being a LongWord.Mamie
I have similar question of using FindWindow function. Could you check it? [#58501270Diehard
G
2

According to Delphi Help, FindWindow(ClassName,WindowName) does not search child windows. This could be the reason for the 1% failures. Maybe in those two beta tester's systems the IEFrame window had WS_CHILD style set.

This would explain why the GetTopWindow/GetNextWindow loop works. GetTopWindow(hWndParent) retrieves the child window at the top of the Z order, and GetNextWindow(hWnd,Direction) retrieves the next child window in the Z order.

This could be tested by calling FindWindowEx(hWndParent,hWndChild,ClassName,WindowName), to see if it works where FindWindow() fails.

Gales answered 22/6, 2018 at 14:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.