Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 7793321
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 1, 20262026-06-01T22:27:01+00:00 2026-06-01T22:27:01+00:00

AS. since closing related questions – more examples added below. The below simple code

  • 0

AS. since closing related questions – more examples added below.

The below simple code (which finds a top-level Ie window and enumerates its children) works Ok with a ’32-bit Windows’ target platform. There’s no problem with earlier versions of Delphi as 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 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;

I’ve inserted an Assert to indicate where it fails with a ’64-bit Windows’ target platform. There’s no problem with the code if I un-nest the callback.

I’m not sure if the erroneous values passed with the parameters are just garbage or are due to some mis-placed memory addresses (calling convention?). Is nesting callbacks infact something that I should never do in the first place? Or is this just a defect that I have to live with?

edit:

In response to David’s answer, the same code having EnumChildWindows declared with a typed callback. Works fine with 32-bit:

(edit: The below does not really test what David says since I still used the ‘@’ operator. It works fine with the operator, but if I remove it, it indeed does not compile unless I un-nest the 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;

Actually this limitation is not specific to a Windows API callbacks, but the same problem happens when taking address of that function into a variable of procedural type and passing it, for example, as a custom comparator to TList.Sort.

http://docwiki.embarcadero.com/RADStudio/Rio/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;

It works as expected when compiled as 32-bit, but fails with Access Violation when compiled for Win64. For 64-bit version in function compare, s = nil and i2 = some random value;

It also works as expected even for Win64 target, if one extracts compare function outside of btn1Click function.

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-01T22:27:03+00:00Added an answer on June 1, 2026 at 10:27 pm

    This trick was never officially supported by the language and you have been getting away with it to date due to the implementation specifics of the 32 bit compiler. The documentation is clear:

    Nested procedures and functions (routines declared within other routines) cannot be used as procedural values.

    If I recall correctly, an extra, hidden, parameter is passed to nested functions with the pointer to the enclosing stack frame. This is omitted in 32 bit code if no reference is made to the enclosing environment. In 64 bit code the extra parameter is always passed.

    Of course a big part of the problem is that the Windows unit uses untyped procedure types for its callback parameters. If typed procedures were used the compiler could reject your code. In fact I view this as justification for the belief that the trick you used was never legal. With typed callbacks a nested procedure can never be used, even in the 32 bit compiler.

    Anyway, the bottom line is that you cannot pass a nested function as parameter to another function in the 64 bit compiler.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I have divs that are initially closed. The code below then controls the opening/closing:
Since input tags aren't supposed to have closing tags, is there a simple way
In C/C++ like languages, closing zeromq socket explicitly is a must, which I understand.
Since closing the html tag is optional , is it okay to include markup
It's been a while since I've had to do any HTML-like code in Vim
I added a modalView to my App, everything working fine, but on closing the
Since the support for BO and Crystal pretty much stinks when it comes to
Since I installed IE8 (Win XP) Visual Studio 2008 Pro stopped deebugging javascript. When
Since version 1.5 Subversion supports to have a local caching-proxy for the main Master-repository.
since IE won't render XHTML as XHTML, but treat it as HTML instead, when

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.