【问题标题】:Delphi XE2 EnumWindows not working properlyDelphi XE2 EnumWindows 无法正常工作
【发布时间】:2012-03-01 18:45:49
【问题描述】:

在 Win7 64 位上使用 Delphi XE2 更新 3 或更新 4。

调用 enumwindows 不像以前在 Delphi 6 中那样工作。

在 Delphi 6 enumwindows 处理窗口直到回调函数返回 False。这就是文档所说的应该做的:

"要继续枚举,回调函数必须返回TRUE;要停止枚举,它必须返回FALSE。"

如下调用 enumwindows:

procedure TForm1.Button1Click(Sender: TObject);
begin
  EnumWindows(@FindMyWindow,0);
  if GLBWindowHandle <> 0 then begin
    ShowMessage('found');
  end;
end;

这里是回调函数:

function FindMyWindow(hWnd: HWND; lParam: LPARAM): boolean; stdcall;
var TheText : array[0..150] of char;
str : string;
begin
Result := True;
GLBWindowHandle := 0;
if (GetWindowText(hWnd, TheText, 150) <> 0) then
   begin
   str := TheText;
   if str = 'Form1' then
      begin
      GLBWindowHandle := hWnd;
      Result := False;
      end
   else
      result := True;
   end;
end;

请注意,回调函数是在 buttonclick 事件之前的代码中定义的,因此编译器可以找到它,而无需在接口部分中定义。

如果这是使用 Delphi 6 运行的,则一旦返回 False 结果且 GLBWindowHandle 不为零,窗口的枚举就会停止

如果使用 Delphi XE2 运行,则在返回 False 结果且 GLBWindowHandle 始终为零后继续枚举。

WTF?任何人都知道为什么枚举没有像文档中所说的那样停止,以及它在 Delphi 6 中是如何停止的?

干杯!

【问题讨论】:

  • 您是否验证了GLBWindowHandle 被设置然后重置,或者它也有可能根本没有被设置?
  • 是的,先设置然后再重置。
  • 如果您针对 32 位和 64 位进行编译,您是否观察到相同的行为?
  • 我们目前只编译 32 位。我没有测试64位编译。

标签: delphi winapi delphi-xe2


【解决方案1】:

此声明不正确:

function FindMyWindow(hWnd: HWND; lParam: LPARAM): boolean; stdcall;

应该是:

function FindMyWindow(hWnd: HWND; lParam: LPARAM): BOOL; stdcall;

您必须小心不要混淆Boolean 和BOOL,因为它们不是同一个东西。前者为单字节,后者为4字节。 EnumWindows 所期望的与您的回调函数提供的不匹配足以导致您观察到的行为。


此外,Rob Kennedy 也发表了这篇精彩的评论:

如果您在调用EnumWindows 时改掉在函数名前使用@ 运算符的习惯,编译器可以帮助您找到此错误。如果函数签名兼容,编译器将允许您在没有@ 的情况下使用它。使用@ 会将其转换为通用指针,并且与所有内容都兼容,因此错误会被不必要的语法掩盖。简而言之,使用@ 创建函数指针应该被认为是一种代码气味。


讨论

不幸的是,Windows.pas 标头翻译以最无益的方式定义了EnumWindows,如下所示:

function EnumWindows(lpEnumFunc: TFNWndEnumProc; lParam: LPARAM): BOOL; stdcall;

现在,问题在于TFNWndEnumProc 的定义。定义为:

TFarProc = Pointer;
TFNWndEnumProc = TFarProc;

这意味着您必须使用@ 运算符来生成通用指针,因为函数需要通用指针。如果TFNWndEnumProc 是这样声明的:

TFNWndEnumProc = function(hWnd: HWND; lParam: LPARAM): BOOL; stdcall;

那么编译器将能够找到错误。

type
  TFNWndEnumProc = function(hWnd: HWND; lParam: LPARAM): BOOL; stdcall;

function EnumWindows(lpEnumFunc: TFNWndEnumProc;
  lParam: LPARAM): BOOL; stdcall; external 'user32';

function FindMyWindow(hWnd: HWND; lParam: LPARAM): Boolean; stdcall;
begin
  Result := False;
end;

....
EnumWindows(FindMyWindow, 0);

编译器拒绝对EnumWindows 的调用,并出现以下错误:

[DCC 错误] Unit1.pas(38): E2010 不兼容的类型:'LongBool' 和 'Boolean'

我想我会 QC 这个问题并尝试说服 Embarcadero 停止使用 TFarProc。

【讨论】:

  • 我猜你只是在 D6 中走运了。我没有尝试找出行为改变的确切原因。
  • 多年来,编译器内部似乎发生了许多事情,这些事情看起来很奇怪。 f.我看到了我自己的问题的答案(这让我难倒了将近一年)(stackoverflow.com/questions/1482699/…)
  • 如果您在调用EnumWindows 时改掉在函数名前使用@ 运算符的习惯,编译器可以帮助您找到此错误。如果函数签名兼容,编译器将允许您在没有@ 的情况下使用它。使用@ 会将其转换为通用指针,并且与所有内容都兼容,因此错误会被不必要的语法掩盖。简而言之,使用@ 创建函数指针应该被认为是一种代码气味。
  • @RobKennedy,我不关注......你的意思是打电话给EnumWindows,例如EnumWindows(FindMyWindow, 0)? (根本不会编译)。
  • 用 D5/D7/D2009 测试。结果:Not enough actual parameters
猜你喜欢
  • 1970-01-01
  • 2013-12-25
  • 1970-01-01
  • 2016-12-01
  • 2014-05-04
  • 1970-01-01
  • 1970-01-01
  • 2017-01-09
  • 2015-08-14
相关资源
最近更新 更多