AS. после закрытия связанных вопросов - больше примеров, добавленных ниже.
Следующий простой код (который находит окно Ie верхнего уровня и перечисляет его дочерние элементы) работает нормально с целевой платформой "32-битная Windows". Там нет проблем с более ранними версиями Delphi:
procedure TForm1.Button1Click(Sender: TObject);
function EnumChildren(hwnd: HWND; lParam: LPARAM): BOOL; stdcall;
const
Server = 'Internet Explorer_Server';
var
ClassName: array[0..24] of Char;
begin
Assert(IsWindow(hwnd)); // <- Assertion fails with 64-bit
GetClassName(hwnd, ClassName, Length(ClassName));
Result := ClassName <> Server;
if not Result then
PUINT_PTR(lParam)^ := hwnd;
end;
var
Wnd, WndChild: HWND;
begin
Wnd := FindWindow('IEFrame', nil); // top level IE
if Wnd <> 0 then begin
WndChild := 0;
EnumChildWindows(Wnd, @EnumChildren, UINT_PTR(@WndChild));
if WndChild <> 0 then
..
end;
Я вставил Assert
, чтобы указать, где он терпит неудачу с целевой платформой" 64-битная Windows". Там нет проблем с кодом, если я разблокирую обратный вызов.
Я не уверен, что ошибочные значения, переданные с параметрами, являются просто мусором или связаны с некоторыми неправильно размещенными адресами памяти (вызов конвенции?). Является ли вложенность обратных вызовов тем, что я никогда не должен делать в первую очередь? Или это просто дефект, с которым я должен жить?
изменить
В ответ на ответ Дэвида тот же код, имеющий EnumChildWindows
, объявленный с типизированным обратным вызовом. Прекрасно работает с 32-битным:
(edit: ниже показано, что Дэвид говорит не так, потому что я все еще использовал оператор @. Он отлично работает с оператором, но если я его удалю, он действительно не компилируется, если только я разблокировать обратный вызов)
type
TFNEnumChild = function(hwnd: HWND; lParam: LPARAM): Bool; stdcall;
function TypedEnumChildWindows(hWndParent: HWND; lpEnumFunc: TFNEnumChild;
lParam: LPARAM): BOOL; stdcall; external user32 name 'EnumChildWindows';
procedure TForm1.Button1Click(Sender: TObject);
function EnumChildren(hwnd: HWND; lParam: LPARAM): BOOL; stdcall;
const
Server = 'Internet Explorer_Server';
var
ClassName: array[0..24] of Char;
begin
Assert(IsWindow(hwnd)); // <- Assertion fails with 64-bit
GetClassName(hwnd, ClassName, Length(ClassName));
Result := ClassName <> Server;
if not Result then
PUINT_PTR(lParam)^ := hwnd;
end;
var
Wnd, WndChild: HWND;
begin
Wnd := FindWindow('IEFrame', nil); // top level IE
if Wnd <> 0 then begin
WndChild := 0;
TypedEnumChildWindows(Wnd, @EnumChildren, UINT_PTR(@WndChild));
if WndChild <> 0 then
..
end;
На самом деле это ограничение не относится к обратным вызовам Windows API, но та же проблема возникает, когда адрес этой функции входит в переменную procedural type
и передает ее, например, как пользовательский компаратор с TList.Sort
.
http://docwiki.embarcadero.com/RADStudio/XE4/en/Procedural_Types
procedure TForm2.btn1Click(Sender: TObject);
var s : TStringList;
function compare(s : TStringList; i1, i2 : integer) : integer;
begin
result := CompareText(s[i1], s[i2]);
end;
begin
s := TStringList.Create;
try
s.add('s1');
s.add('s2');
s.add('s3');
s.CustomSort(@compare);
finally
s.free;
end;
end;
Он работает как ожидалось, когда скомпилирован как 32-разрядный, но с компиляцией для Win64 с ошибкой Access Violation
. Для 64-битной версии в функции compare
, s = nil
и i2
= некоторое случайное значение;
Он также работает так же, как и для целевого объекта Win64, если вы извлекаете compare
функцию вне функции btn1Click
.