AS. desde el cierre de preguntas relacionadas - más ejemplos se agregan a continuación.¿Por qué no se puede llevar la dirección a una función local anidada en Delphi de 64 bits?
La continuación código simple (que encuentra una ventana de IE de nivel superior y enumera sus hijos) funciona bien con un '32 bits de Windows plataforma de destino. No hay ningún problema con las versiones anteriores de Delphi, así:
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;
he insertado una Assert
para indicar dónde se produce un error con un '64 bits de Windows plataforma de destino. No hay problema con el código si anula la devolución de llamada.
no estoy seguro de si los valores erróneos pasados con los parámetros son sólo basura o se deben a algunas direcciones de memoria erróneas colocados convención de llamada (?). ¿Las devoluciones de llamadas de anidación son algo que nunca debería hacer en primer lugar? ¿O es solo un defecto con el que tengo que vivir?
edición:
En respuesta a la respuesta de David, el mismo código que tiene EnumChildWindows
declarada con una devolución de llamada con tipo. Funciona bien con 32 bits:
(edit: Lo que sigue no prueba realmente lo que David dice ya que todavía uso el operador '@'. Funciona bien con el operador, pero si lo elimino, de hecho no lo hace compilado a menos que un-nido de la devolución de llamada)
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;
en realidad esta limitación no es específico de una devoluciones de llamada API de Windows, pero el mismo problema ocurre cuando se toma la dirección de dicha función en una variable de procedural type
y pasarlo, por ejemplo, como un comparador personalizado al 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;
Funciona como se esperaba cuando se compila como de 32 bits, pero falla con Access Violation
cuando se compila para Win64. Para la versión de 64 bits en función compare
, s = nil
y i2
= alguna valor aleatorio;
También funciona como se esperaba, incluso para el objetivo Win64, si se extrae la función compare
fuera de la función btn1Click
.
Si el código no anidado es funcionalmente equivalente al anidado, entonces es un error de compilación o un problema de devolución de parámetros de devolución de llamada. Voto por este último, es posible que esté obteniendo daños en la pila ya que la convención de llamadas de 64 bits es diferente a las de 32 bits, por lo que quizás "stdcall" no sea lo que debería usar aquí. Intente eliminarlo y vea si sucede nuevamente. De lo contrario, las devoluciones de llamadas de anidación están perfectamente bien (al menos en la forma que se muestra aquí). – Thomas
SÓLO HAY UNA convención de llamadas en Win64; Por lo tanto, cualquier convención de llamadas que especifique en el código se ignora en el modo de 64 bits. Sin embargo, su firma de función/procedimiento/dll-import podría estar equivocada, y eso podría corromper las cosas. Sin embargo, la imposibilidad de que Delphi implemente una devolución de llamada desde una función suena como un error del compilador. –
@Thomas - La aserción falla nuevamente cuando elimino 'stdcall', supongo que el compilador Delphi simplemente lo ignora cuando apunta a 64 bits. –