为什么不能在64位Delphi中获取嵌套本地函数的地址?

为什么不能在64位Delphi中获取嵌套本地函数的地址?,delphi,delegates,nested,delphi-xe2,32bit-64bit,Delphi,Delegates,Nested,Delphi Xe2,32bit 64bit,作为。自结束相关问题以来-下面添加了更多示例 下面的简单代码(查找顶级Ie窗口并枚举其子窗口)适用于“32位Windows”目标平台。早期版本的Delphi也没有问题: procedure TForm1.Button1Click(Sender: TObject); function EnumChildren(hwnd: HWND; lParam: LPARAM): BOOL; stdcall; const Server = 'Internet Explorer_Server';

作为。自结束相关问题以来-下面添加了更多示例

下面的简单代码(查找顶级Ie窗口并枚举其子窗口)适用于“32位Windows”目标平台。早期版本的Delphi也没有问题:

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;

实际上,此限制并不特定于Windows API回调,但当将该函数的地址放入
过程类型的变量中并将其作为自定义比较器传递给
TList.Sort
时,也会出现相同的问题

当编译为32位时,它可以正常工作,但在为Win64编译时,由于访问冲突而失败。对于函数
中的64位版本,比较
s=nil
i2
=一些随机值


如果在
btn1Click
函数之外提取
compare
函数,即使对于Win64目标,它也能像预期的那样工作。

这种技巧从未得到该语言的正式支持,而且由于32位编译器的实现细节,迄今为止您一直没有使用它。问题很清楚:

嵌套过程和函数(在其他例程中声明的例程)不能用作过程值

如果我没记错的话,一个额外的隐藏参数被传递给嵌套函数,指针指向封装的堆栈框架。如果未引用封闭环境,则在32位代码中会忽略此项。在64位代码中,始终传递额外的参数

当然,问题的很大一部分是Windows单元使用非类型化过程类型作为其回调参数。如果使用类型化过程,编译器可能会拒绝您的代码。事实上,我认为这是你所使用的伎俩永远都不合法的理由。对于类型化回调,嵌套过程永远不能使用,即使在32位编译器中也是如此


无论如何,底线是您不能将嵌套函数作为参数传递给64位编译器中的另一个函数。

该语言从未正式支持此技巧,并且由于32位编译器的实现细节,您迄今为止一直没有使用它。问题很清楚:

嵌套过程和函数(在其他例程中声明的例程)不能用作过程值

如果我没记错的话,一个额外的隐藏参数被传递给嵌套函数,指针指向封装的堆栈框架。如果未引用封闭环境,则在32位代码中会忽略此项。在64位代码中,始终传递额外的参数

当然,问题的很大一部分是Windows单元使用非类型化过程类型作为其回调参数。如果使用类型化过程,编译器可能会拒绝您的代码。事实上,我认为这是你所使用的伎俩永远都不合法的理由。对于类型化回调,嵌套过程永远不能使用,即使在32位编译器中也是如此


无论如何,底线是不能将嵌套函数作为参数传递给64位编译器中的另一个函数。

如果未嵌套的代码在功能上等同于嵌套的代码,则可能是编译器错误或回调参数传递问题。我赞成后者,因为64位调用约定不同于32位调用约定,所以可能会出现堆栈损坏,所以这里不应该使用“stdcall”。尝试移除它,看看是否再次发生。否则嵌套回调就完全可以了(至少以这里显示的方式);因此,您在代码中指定的任何调用约定在64位模式下都会被忽略。但是,您的函数/过程/dll导入签名可能是错误的,这可能会损坏某些东西。然而,无法让Delphi实现来自函数的回调听起来确实像是编译器的错误。@Thomas-当我删除“stdcall”时,断言再次失败,我想Delphi编译器只是在64位为目标时忽略了它。请注意,您正在将指向HWND的指针强制转换为一个4字节的长字。您应该在x64上使用NativeInt,或者只传递已经是8字节无符号整数(NativeUInt)的HWND。我们发现这不仅是winapi回调的问题,也是传递本地函数/过程作为对我们自己的Delphi代码的回调的问题,特别是当涉及多线程时。我记不清具体细节,但它确实与编译器如何处理本地方法、插入/不插入的内容以及触发回调时堆栈上可用的内容有关。如果未嵌套的代码在功能上与嵌套的代码等效,这要么是编译器错误,要么是回调参数传递问题。我赞成后者,因为64位调用约定不同于32位调用约定,所以可能会出现堆栈损坏,所以这里不应该使用“stdcall”。尝试移除它,看看是否再次发生。否则嵌套回调就完全可以了(至少以这里显示的方式);因此,您在代码中指定的任何调用约定在64位模式下都会被忽略。但是,您的函数/过程/dll导入签名可能是错误的,这可能会损坏某些东西。然而,无法让Delphi实现来自函数的回调听起来确实像是编译器的错误。@Thomas-当我删除“stdcall”时,断言再次失败,我想Delphi编译器只是在64位为目标时忽略了它。请注意,您正在将指向HWND的指针强制转换为一个4字节的长字。您应该在x64上使用NativeInt,或者只传递已经是8字节无符号整数(NativeUInt)的HWND。我们发现这不仅是winapi回调的问题,也是传递本地函数/过程作为对我们自己的Delphi代码的回调的问题,特别是当涉及多线程时。我
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;
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;