¿Por qué no se puede llevar la dirección a una función local anidada en Delphi de 64 bits?

COMO. desde el cierre de preguntas relacionadas - más ejemplos añadidos a continuación.

El siguiente código simple (que encuentra una ventana de nivel superior, es decir, enumera sus hijos) funciona bien con una plataforma de destino de Windows de 32 bits. No hay problema con las versiones anteriores de Delphi también:

<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>


He insertado unAssert para indicar dónde falla con una plataforma de destino de Windows de 64 bits. No hay problema con el código sides-nido la devolución de llamada.

No estoy seguro de si los valores erróneos pasados ​​con los parámetros son simplemente basura o se deben a algunas direcciones de memoria mal colocadas (¿convención de llamada?). ¿Las devoluciones de llamadas de anidación afectan a algo que nunca debería hacer en primer lugar? ¿O es solo un defecto con el que tengo que vivir?

editar:
En respuesta a la respuesta de David, el mismo código que tieneEnumChildWindows declarado con una devolución de llamada mecanografiada. Funciona bien con 32 bits:

(corrija: lo que sigue a continuación no prueba realmente lo que dice David ya que todavía usé el operador '@'. Funciona bien con el operador, pero si lo quito, de hecho no se compila a menos que desactive la devolución de llamada)

<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>

En realidad, esta limitación no es específica de las devoluciones de llamada de la API de Windows, pero ocurre el mismo problema cuando se toma la dirección de esa función en una variable deprocedural type y pasarlo, por ejemplo, como un comparador personalizado paraTList.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>

Funciona como se espera cuando se compila como de 32 bits, pero falla conAccess Violation cuando se compila para Win64. Para versión de 64 bits en función.compare, s = nil yi2 = algún valor aleatorio;

También funciona como se espera incluso para el objetivo de Win64, si se extraecompare función fuera debtn1Click función.

Respuestas a la pregunta(1)

Su respuesta a la pregunta