Delphi XE2 EnumWindows not working properly

Using Delphi XE2 update 3 or update 4 on 64-bit Win7.

The enumwindows call does not work as it was used to work in Delphi 6.

In Delphi 6 processed windows, enumwindows until the callback function returns False. Here is what the documentation says:

"To continue the enumeration, the callback function must return TRUE; to stop the enumeration, it must return FALSE."

Making an enumwindows call 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 the code BEFORE the buttonclick event, so it is found by the compiler without requiring a definition in the interface section.

If this is done using Delphi 6, window enumeration stops after returning False and GLBWindowHandle is not zero

If this is done using Delphi XE2, the enumeration continues after returning False and GLBWindowHandle is always zero.

WTF? Does anyone have any ideas why the listing doesn't stop, as the documentation states, and how is it used in Delphi 6?

Hurrah!

+6
source share
1 answer

This declaration is incorrect:

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

It should be:

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

You must be careful not to confuse Boolean and BOOL , as they are not the same. The first is one byte, the last is 4 bytes. This is a mismatch between the expected EnumWindows and what the callback function provides to cause the observed behavior.


In addition, Rob Kennedy made a wonderful comment:

The compiler can help find this error if you break the habit of using the @ operator before the function name when calling EnumWindows . If the function signature is compatible, the compiler will allow you to use it without @ . Using @ turns it into a generic pointer and is 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, translating the Windows.pas header defines EnumWindows most useless way:

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

Now the problem is defining TFNWndEnumProc . It is defined as:

 TFarProc = Pointer; TFNWndEnumProc = TFarProc; 

This means that you must use the @ operator to create a shared pointer, because functions require a shared pointer. If TFNWndEnumProc were declared as follows:

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

then the compiler could 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 EnumWindows call with the following error:

[DCC error] Unit1.pas (38): E2010 Incompatible types: "LongBool" and "Boolean"

I think I will be QC this problem and try my luck convincing Embarcadero to stop using TFarProc .

+12
source

Source: https://habr.com/ru/post/909770/


All Articles