Using Delphi XE2 update 3 or update 4 on Win7 64 bit.
Calling enumwindows does not work like it used to work in Delphi 6.
In Delphi 6 enumwindows processed windows until the callback function returned False. That is what the documentation says it should do:
"To continue enumeration, the callback function must return TRUE; to stop enumeration, it must return FALSE."
Making a call to enumwindows as follows:
procedure TForm1.Button1Click(Sender: TObject);
begin
EnumWindows(@FindMyWindow,0);
if GLBWindowHandle <> 0 then begin
ShowMessage('found');
end;
end;
Here is the callback function:
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;
Just to be clear the callback function is defined in code BEFORE the buttonclick event so it is found by the compiler without needing to be defined in the interface section.
If this is run using Delphi 6 the enumeration of windows stops once the False result is returned and GLBWindowHandle is not zero
If this is run using Delphi XE2 the enumeration continues after the False result is returned and GLBWindowHandle is always zero.
WTF? Anybody have any ideas why the enumeration is not stopping like the documentation states it should and how it used to in Delphi 6?
Cheers!
This declaration is incorrect:
function FindMyWindow(hWnd: HWND; lParam: LPARAM): boolean; stdcall;
It should be:
function FindMyWindow(hWnd: HWND; lParam: LPARAM): BOOL; stdcall;
You have to be careful not to mix up Boolean
and BOOL
since they are not the same thing. The former is a single byte, the latter is 4 bytes. This mismatch between what EnumWindows
expects and what your callback function delivers is enough to cause the behaviour you observe.
In addition, Rob Kennedy contributed this excellent comment:
The compiler can help find this error if you get out of the habit of using the
@
operator before the function name when you callEnumWindows
. If the function signature is compatible, the compiler will let you use it without@
. Using@
turns it into a generic pointer, and that's compatible with everything, so the error is masked by unnecessary syntax. In short, using@
to create function pointers should be considered a code smell.
Discussion
Unfortunately the Windows.pas
header translation defines EnumWindows
in a most unhelpful manner, like this:
function EnumWindows(lpEnumFunc: TFNWndEnumProc; lParam: LPARAM): BOOL; stdcall;
Now, the problem is in the definition of TFNWndEnumProc
. It is defined as:
TFarProc = Pointer;
TFNWndEnumProc = TFarProc;
This means that you have to use the @
operator to make a generic pointer, because the function needs a generic pointer. If TFNWndEnumProc
were declared like this:
TFNWndEnumProc = function(hWnd: HWND; lParam: LPARAM): BOOL; stdcall;
then the compiler would have been able to find the error.
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);
The compiler rejects the call to EnumWindows
with the following error:
[DCC Error] Unit1.pas(38): E2010 Incompatible types: 'LongBool' and 'Boolean'
I think I will QC this issue and try my luck at persuading Embarcadero to stop using TFarProc
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With