Warum kann in 64-Bit-Delphi keine Adresse an eine verschachtelte lokale Funktion übergeben werden?

WIE. seit dem Schließen verwandter Fragen - weitere Beispiele unten hinzugefügt.

Der folgende einfache Code (der ein Fenster der obersten Ebene findet und dessen untergeordnete Elemente auflistet) funktioniert auf einer Zielplattform mit 32-Bit-Windows einwandfrei. Auch mit früheren Delphi-Versionen gibt es keine Probleme:

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


Ich habe eine eingefügtAssert um anzuzeigen, wo ein Fehler mit einer 64-Bit-Windows-Zielplattform auftritt. Es gibt kein Problem mit dem Code, wenn ichNest auflösen der Rückruf.

Ich bin nicht sicher, ob die mit den Parametern übergebenen fehlerhaften Werte nur Müll sind oder auf falsch platzierte Speicheradressen zurückzuführen sind (Aufrufkonvention?). Ist das Verschachteln von Rückrufen etwas, was ich niemals tun sollte? Oder ist das nur ein Defekt, mit dem ich leben muss?

bearbeiten:
Als Antwort auf Davids Antwort hatte derselbe CodeEnumChildWindows deklariert mit einem getippten Rückruf. Funktioniert gut mit 32-Bit:

(edit: Das Folgende testet nicht wirklich, was David sagt, da ich immer noch den '@' Operator verwendet habe. Es funktioniert gut mit dem Operator, aber wenn ich es entferne, wird es in der Tat nicht kompiliert, es sei denn, ich hebe die Verschachtelung des Rückrufs auf)

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

Tatsächlich ist diese Einschränkung nicht spezifisch für Windows-API-Rückrufe, aber das gleiche Problem tritt auf, wenn die Adresse dieser Funktion in eine Variable von übernommen wirdprocedural type und übergeben Sie es beispielsweise als benutzerdefinierten Komparator anTList.Sort.

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

Es funktioniert wie erwartet, wenn es als 32-Bit-Version kompiliert wird, schlägt jedoch mit fehlAccess Violation wenn für Win64 kompiliert. Für 64-Bit-Version in Funktioncompare, s = nil undi2 = ein zufälliger Wert;

Es funktioniert auch wie erwartet, auch für Win64-Ziel, wenn man extrahiertcompare Funktion außerhalb vonbtn1Click Funktion.

Antworten auf die Frage(1)

Ihre Antwort auf die Frage