Delphi XE2 EnumWindows не работает должным образом - PullRequest
6 голосов
/ 01 марта 2012

Использование Delphi XE2 с обновлением 3 или 4 в Win7 64 бит.

Вызов enumwindows не работает так же, как и в Delphi 6.

В Delphi 6 enumwindows обрабатывает окна до тех пор, покафункция обратного вызова вернула False.Вот что говорится в документации:

"Чтобы продолжить перечисление, функция обратного вызова должна вернуть TRUE; чтобы остановить перечисление, она должна вернуть FALSE."

Выполнение вызова enumwindows asследующим образом:

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, перечисление окон останавливается, когда возвращается ложный результат, а GLBWindowHandle не равен нулю

Если это выполняется с использованием Delphi XE2перечисление продолжается после того, как возвращается ложный результат, и GLBWindowHandle всегда равен нулю.

WTF?У кого-нибудь есть идеи, почему перечисление не останавливается, как указано в документации, и как это было в Delphi 6?

Cheers!

1 Ответ

12 голосов
/ 01 марта 2012

Это объявление неверно:

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

Должно быть:

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

Вы должны быть осторожны, чтобы не перепутать Boolean и BOOL, поскольку они не одно и то же. Первый - это один байт, последний - 4 байта. Этого несоответствия между тем, что ожидает EnumWindows, и тем, что обеспечивает ваша функция обратного вызова, достаточно, чтобы вызвать поведение, которое вы наблюдаете.


Кроме того, Роб Кеннеди внес этот превосходный комментарий:

Компилятор может помочь найти эту ошибку, если вы избавитесь от привычки использовать оператор @ перед именем функции при вызове 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»

Думаю, я проверю эту проблему и попытаюсь убедить Embarcadero прекратить использование TFarProc.

...