Delphi XE2 EnumWindows无法正常工作

TJ *_*her 6 delphi winapi delphi-xe2

在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;
Run Code Online (Sandbox Code Playgroud)

这是回调函数:

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;
Run Code Online (Sandbox Code Playgroud)

为了清楚起见,回调函数在按钮单击事件之前的代码中定义,因此编译器可以找到它而无需在接口部分中定义.

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

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

WTF?任何人都有任何想法为什么枚举不会停止像文档说明它应该以及如何在Delphi 6中使用?

干杯!

Dav*_*nan 12

此声明不正确:

function FindMyWindow(hWnd: HWND; lParam: LPARAM): boolean; stdcall;
Run Code Online (Sandbox Code Playgroud)

它应该是:

function FindMyWindow(hWnd: HWND; lParam: LPARAM): BOOL; stdcall;
Run Code Online (Sandbox Code Playgroud)

你必须要小心,不要混淆Boolean,BOOL因为它们不是一回事.前者是单字节,后者是4字节.EnumWindows期望与回调函数之间的不匹配足以导致您观察到的行为.


此外,Rob Kennedy贡献了这一优秀评论:

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


讨论

不幸的是,Windows.pas标题转换EnumWindows以最无益的方式定义,如下所示:

function EnumWindows(lpEnumFunc: TFNWndEnumProc; lParam: LPARAM): BOOL; stdcall;
Run Code Online (Sandbox Code Playgroud)

现在,问题在于定义TFNWndEnumProc.它被定义为:

TFarProc = Pointer;
TFNWndEnumProc = TFarProc;
Run Code Online (Sandbox Code Playgroud)

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

TFNWndEnumProc = function(hWnd: HWND; lParam: LPARAM): BOOL; stdcall;
Run Code Online (Sandbox Code Playgroud)

然后编译器就能找到错误.

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);
Run Code Online (Sandbox Code Playgroud)

编译器拒绝调用EnumWindows以及以下错误:

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

我想我会质疑这个问题并试着说服Embarcadero停止使用TFarProc.

  • 如果你不习惯在调用`EnumWindows`时在函数名之前使用`@`运算符,编译器可以帮助找到这个错误.如果函数签名兼容,编译器将允许您使用它而不使用`@`.使用`@`将其转换为通用指针,并且与所有内容兼容,因此错误会被不必要的语法所掩盖.简而言之,使用`@`创建函数指针应该被视为*代码气味*. (3认同)