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?

Casady
  • 1,426
  • 3
  • 19
  • 39
  • 1
    There are some remarks on the [FindWindow](http://msdn.microsoft.com/en-us/library/windows/desktop/ms633499(v=vs.85).aspx) and [GetWindowText](http://msdn.microsoft.com/en-us/library/windows/desktop/ms633520(v=vs.85).aspx) pages on MSDN that might provide some clues. – 500 - Internal Server Error Mar 18 '13 at 05:03
  • 9
    Instead of looping through the windows manually, you should use [`EnumWindows()`](http://msdn.microsoft.com/en-us/library/windows/desktop/ms633497.aspx) instead. – Remy Lebeau Mar 18 '13 at 05:14
  • 1
    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%. – Casady Mar 18 '13 at 05:27
  • 5
    What is inline code? You can use EnumWindows anywhere you can use FindWindow. – David Heffernan Mar 18 '13 at 07:38
  • 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. – Casady Mar 18 '13 at 07:57
  • 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? – David Heffernan Mar 18 '13 at 09:03
  • 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. – Casady Mar 18 '13 at 09:26
  • @Casady No. That's incorrect. A call to `EnumWindows` does not return until all callbacks have been called. – David Heffernan Mar 18 '13 at 09:29
  • 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! – Casady Mar 18 '13 at 09:29
  • 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. – Casady Mar 18 '13 at 09:39
  • 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. – TLama Mar 18 '13 at 09:43
  • 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. – Casady Mar 18 '13 at 09:48
  • 2
    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`. – David Heffernan Mar 18 '13 at 09:51
  • 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. – David Heffernan Mar 18 '13 at 09:52
  • 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. – Casady Mar 18 '13 at 10:05
  • 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. – David Heffernan Mar 18 '13 at 10:30
  • Have you tested it multiple times? Because sometimes I get an execution time of near 0ms, but sometimes up to 25 ms. – Casady Mar 18 '13 at 10:36
  • 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! – David Heffernan Mar 18 '13 at 11:01
  • I wrote 4 comments before that I've made 10 runs, of which 4 were above 15ms, the rest near 0. – Casady Mar 18 '13 at 11:05
  • 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. – David Heffernan Mar 18 '13 at 11:15

2 Answers2

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.

arx
  • 16,686
  • 2
  • 44
  • 61
  • 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. – Casady Mar 19 '13 at 04:50
  • arx, I've checked the definition of HWND in Delphi 7: HWND = type LongWord; so it cannot be negative. – Casady Mar 19 '13 at 06:04
  • 2
    @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. – David Heffernan Mar 19 '13 at 07:54
  • @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? – arx Mar 19 '13 at 09:56
  • 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. – Casady Mar 19 '13 at 11:31
  • I have similar question of using FindWindow function. Could you check it? [https://stackoverflow.com/questions/58500770/control-other-application-by-using-delphi-application] – Thet Cartter Nov 20 '19 at 09:19
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.

Guy Gordon
  • 740
  • 6
  • 13