AS. dalla chiusura di domande correlate - altri esempi aggiunti di seguito.Perché non è possibile prendere l'indirizzo di una funzione locale nidificata in Delphi a 64 bit?
Il seguente codice semplice (che trova una finestra di Ie di primo livello e enumera i suoi figli) funziona correttamente con una piattaforma di destinazione '32 -bit Windows '. Non c'è nessun problema con le versioni precedenti di Delphi così:
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;
ho inserito un Assert
per indicare dove non riesce con un '64 bit piattaforma di destinazione di Windows'. Non c'è alcun problema con il codice se io disattivi il il callback.
Non sono sicuro se i valori errati passati con i parametri siano solo inutili o siano dovuti ad alcuni indirizzi di memoria errati (convenzione di chiamata?). La nidificazione dei callback è qualcosa che non dovrei mai fare in primo luogo? O è solo un difetto con cui devo convivere?
edit:
In risposta alla risposta di David, lo stesso codice avendo EnumChildWindows
dichiarato con un callback digitato. Funziona bene con 32-bit:
(modifica: Il sotto non verifica realmente ciò che David dice dal momento che ho ancora utilizzato l'operatore '@'. Funziona perfettamente con l'operatore, ma se lo rimuovo, in effetti non lo fa compilare meno che un-nido callback)
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;
realtà questa limitazione non è specifico di un callback API di Windows, ma lo stesso problema accade quando prende indirizzo di tale funzione in una variabile di procedural type
e passandolo, ad esempio, come comparatore personalizzato a 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;
funziona come previsto quando compilato come a 32 bit, ma non riesce con Access Violation
quando compilato per Win64. Per la versione a 64 bit nella funzione compare
, s = nil
e i2
= un valore casuale;
Funziona anche come previsto anche per target Win64, se si estrae la funzioneal di fuori della funzione btn1Click
.
Se il codice non annidato è funzionalmente equivalente a quello annidato, è un errore del compilatore o un problema di passaggio dei parametri di callback. Io voto per il secondo, si potrebbe ottenere il danneggiamento dello stack in quanto la convenzione di chiamata a 64 bit è diversa da quella a 32 bit, quindi forse "stdcall" non è quello che dovresti usare qui. Prova a rimuoverlo e vedi se succede di nuovo. Altrimenti, le callback di nidificazione sono perfette (almeno nel modo mostrato qui). – Thomas
C'è solo una convenzione di chiamata in Win64; Quindi qualsiasi convenzione di chiamata specificata nel codice viene ignorata in modalità 64 bit. Tuttavia la funzione/procedura/firma di importazione dll potrebbe essere errata e ciò potrebbe corrompere le cose. Tuttavia l'impossibilità di avere Delphi implementare un callback da una funzione sembra un errore del compilatore. –
@Thomas - L'asserzione fallisce di nuovo quando rimuovo 'stdcall', suppongo che il compilatore Delphi lo ignori solo quando viene indirizzato a 64 bit. –