为什么FindWindow()不是100%可靠的?

11

我正在使用这个Delphi 7代码来检测Internet Explorer是否在运行:

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

这适用于99%的IE 8、9和10系统。

但是有一些系统(不幸的是我的系统中没有,但我有两个测试人员的系统有这样的问题,它们都是Win7 x64 SP1)FindWindow() 在 IEFrame 上返回 0,即使 IE 在内存中也是如此。

因此,我编写了一种替代方法来查找窗口:

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;

备用方法可在所有系统上100%工作。

我的问题是,为什么FindWindow()在某些系统上不可靠?


1
MSDN上的FindWindowGetWindowText页面上有一些注释,可能会提供一些线索。 - 500 - Internal Server Error
9
你应该使用EnumWindows()而不是手动循环窗口。 - Remy Lebeau
1
Remy,EnumWindows()需要一个回调函数,因此不能在内联代码中使用。在这种情况下需要事件处理。并且无论如何,在调用EnumWindows()之后我都必须循环。我在其他应用程序中使用EnumWindows(),而且它也相当慢。但是值得一提的是,这仍然无法解释为什么FindWindow()在99%的系统上可用,但在1%的系统上不可用。 - Casady
5
什么是内联代码?您可以在任何可以使用FindWindow的地方使用EnumWindows。 - David Heffernan
2
我很难相信EnumWindows比GetTopWindow/GetNextWindow慢。这不仅是因为你显然不理解EnumWindow的工作原理,而且我无法想象你真正对其进行了基准测试。此外,众所周知,GetNextWindow存在问题。MSDN中有记录:EnumChildWindows函数比在循环中调用GetWindow更可靠。我预计EnumWindows也是如此。 - David Heffernan
显示剩余15条评论
2个回答

3
我猜测FindWindow被声明为返回一个WinHandle,它是一个THandle,它是一个有符号的整数。(至少在我编写Delphi程序的几年前是这样的。)如果IE有带有设置顶部位的窗口句柄,那么它将是负数,所以你的测试将返回False:
Result := FindWindow('IEFrame', NIL) > 0;

窗口句柄通常不会设置顶部位,但我不确定这是不可能的。


我相当确定WinControl句柄由Microsoft定义为typedef void * HANDLE; - 因此它们不能为负数。我有什么遗漏吗?但你可能是对的。我得去检查一下。 - Casady
arx,我已经检查了Delphi 7中HWND的定义:HWND = type LongWord; 所以它不能为负数。 - Casady
2
如果是这样的话,那么>与<>相同,但是你为什么选择>呢?我惊讶地发现这是一些人经常使用的东西。如果窗口未找到,则该函数返回0。因此,= 0的否定是<> 0。我无法想象什么逻辑会导致> 0。 - David Heffernan
@casady 你说你运行了一个测试程序来复现这个问题。这个程序输出的是FindWindow返回的窗口句柄还是仅仅比较它与0的结果?为了确认一下,你使用的导入单元中是否声明了FindWindow实际上返回一个HWND? - arx
测试程序没有显示句柄,只显示了所有顶层窗口的类名和标题以及FindWindow()函数的结果。不幸的是,两个测试人员昨天都升级到了Internet Explorer 10,问题就解决了,所以我必须等到另一个用户在IE9和我的应用程序旧版本中遇到这个问题。至于FindWindow声明,它被声明为函数FindWindow(lpClassName, lpWindowName: PChar): HWND;,其中HWND是LongWord类型。 - Casady
我有一个类似的问题,涉及到使用FindWindow函数。你能帮忙看一下吗?[https://stackoverflow.com/questions/58500770/control-other-application-by-using-delphi-application] - Thet Cartter

2
根据Delphi帮助文档,FindWindow(ClassName,WindowName)不会搜索子窗口。这可能是1%失败的原因。也许在这两个测试人员的系统中,IEFrame窗口设置了WS_CHILD样式。
这就解释了为什么GetTopWindow/GetNextWindow循环起作用。 GetTopWindow(hWndParent)检索Z顺序中最上面的子窗口,而GetNextWindow(hWnd,Direction)检索Z顺序中的下一个子窗口。
可以通过调用FindWindowEx(hWndParent,hWndChild,ClassName,WindowName)进行测试,看看它是否能够在FindWindow()失败的情况下正常工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接