Почему нельзя взять адрес для вложенной локальной функции в 64-битной Delphi?

КАК. после закрытия связанных вопросов - больше примеров добавлено ниже.

Приведенный ниже простой код (который находит окно Ie верхнего уровня и перечисляет его дочерние элементы) работает нормально с «32-битной Windows». целевая платформа. Нет проблем и с более ранними версиями Delphi:

<code>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;
</code>


Я вставилAssert чтобы указать, где происходит сбой, в «64-битной Windows»; целевая платформа. Нет проблем с кодом, если яun-nest обратный звонок.

Я не уверен, являются ли ошибочные значения, передаваемые с параметрами, просто мусором или они вызваны неправильным размещением адресов памяти (соглашение о вызовах?). Действительно ли вложенные обратные вызовы влияют на то, что я никогда не должен делать? Или это просто дефект, с которым мне приходится жить?

edit:
В ответ на ответ Дэвида, тот же код, имеющийEnumChildWindows объявлен с типизированным обратным вызовом. Работает нормально с 32-битным:

(edit: The below does not really test what David says since I still used the '@' operator. It works fine with the operator, but if I remove it, it indeed does not compile unless I un-nest the callback)

<code>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;
</code>

На самом деле это ограничение не относится к обратным вызовам Windows API, но такая же проблема возникает, когда адрес этой функции переносится в переменнуюprocedural type и передать его, например, в качестве пользовательского компаратораTList.Sort.

http://docwiki.embarcadero.com/RADStudio/XE4/en/Procedural_Types

<code>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;
</code>

Он работает, как и ожидалось, когда скомпилирован как 32-битный, но не сAccess Violation когда скомпилировано для Win64. Для 64-битной версии в функцииcompare, s = nil  а такжеi2 = некоторое случайное значение;

Он также работает, как и ожидалось, даже для Win64 target, если один извлекаетcompare функционировать внеbtn1Click функция.

Ответы на вопрос(1)

Ваш ответ на вопрос