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