delegates - Why cannot take address to a nested local function in 64 bit Delphi? -
as. since closing related questions - more examples added below.
the below simple code (which finds top-level ie window , enumerates children) works ok '32-bit windows' target platform. there's no problem earlier versions of delphi well:
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 64-bit getclassname(hwnd, classname, length(classname)); result := classname <> server; if not result puint_ptr(lparam)^ := hwnd; end; var wnd, wndchild: hwnd; begin wnd := findwindow('ieframe', nil); // top level ie if wnd <> 0 begin wndchild := 0; enumchildwindows(wnd, @enumchildren, uint_ptr(@wndchild)); if wndchild <> 0 .. end;
i've inserted assert indicate fails '64-bit windows' target platform. there's no problem code if un-nest callback.
i'm not sure if erroneous values passed parameters garbage or due mis-placed memory addresses (calling convention?). nesting callbacks infact should never in first place? or defect have live with?
edit:
in response david's answer, same code having enumchildwindows declared typed callback. works fine 32-bit:
(edit: below not test david says since still used '@' operator. works fine operator, if remove it, indeed not compile unless un-nest 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 64-bit getclassname(hwnd, classname, length(classname)); result := classname <> server; if not result puint_ptr(lparam)^ := hwnd; end; var wnd, wndchild: hwnd; begin wnd := findwindow('ieframe', nil); // top level ie if wnd <> 0 begin wndchild := 0; typedenumchildwindows(wnd, @enumchildren, uint_ptr(@wndchild)); if wndchild <> 0 .. end; actually limitation not specific windows api callbacks, same problem happens when taking address of function variable of procedural type , passing it, example, custom comparator 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); s.free; end; end; it works expected when compiled 32-bit, fails access violation when compiled win64. 64-bit version in function compare, s = nil , i2 = random value;
it works expected win64 target, if 1 extracts compare function outside of btn1click function.
this trick never officially supported language , have been getting away date due implementation specifics of 32 bit compiler. documentation clear:
nested procedures , functions (routines declared within other routines) cannot used procedural values.
if recall correctly, extra, hidden, parameter passed nested functions pointer enclosing stack frame. omitted in 32 bit code if no reference made enclosing environment. in 64 bit code parameter passed.
of course big part of problem windows unit uses untyped procedure types callback parameters. if typed procedures used compiler reject code. in fact view justification belief trick used never legal. typed callbacks nested procedure can never used, in 32 bit compiler.
anyway, bottom line cannot pass nested function parameter function in 64 bit compiler.
Comments
Post a Comment