Por que não pode levar o endereço para uma função local aninhada em Delphi de 64 bits?

COMO. desde o fechamento de perguntas relacionadas - mais exemplos adicionados abaixo.

O código simples abaixo (que encontra uma janela Ie de nível superior e enumera seus filhos) funciona Ok com uma plataforma de destino 'de 32 bits do Windows'. Não há problema com versões anteriores do Delphi também:

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


Eu inseri umAssert para indicar onde ele falha com uma plataforma de destino '64 bits do Windows '. Não há problema com o código se euaninhamento o retorno de chamada.

Não tenho certeza se os valores errados passados ​​com os parâmetros são apenas lixo ou são devidos a alguns endereços de memória mal colocados (convenção de chamada?). Os retornos de chamada de aninhamento infactam algo que eu nunca deveria fazer em primeiro lugar? Ou isso é apenas um defeito que eu tenho que viver?

editar:
Em resposta à resposta de David, o mesmo código tendoEnumChildWindows declarado com um retorno de chamada digitado. Funciona bem com 32 bits:

(edit: O abaixo não realmente testar o que David diz desde que eu ainda usei o operador '@'. Ele funciona bem com o operador, mas se eu removê-lo, ele realmente não compila a menos que eu desative o retorno de chamada)

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

Na verdade, essa limitação não é específica para os retornos de chamada da API do Windows, mas o mesmo problema ocorre ao se levar o endereço dessa função para uma variável deprocedural type e passá-lo, por exemplo, como um 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>

Ele funciona como esperado quando compilado como 32 bits, mas falha comAccess Violation quando compilado para Win64. Para versão de 64 bits em funçãocompare, s = nil ei2 = algum valor aleatório;

Ele também funciona como esperado até mesmo para o destino Win64, se um extraircompare função fora debtn1Click função.

questionAnswers(1)

yourAnswerToTheQuestion