Delphi XE2:调用WinAPI EnumResourceNames在Win64平台中导致访问冲突

Delphi XE2:调用WinAPI EnumResourceNames在Win64平台中导致访问冲突,delphi,delphi-xe2,Delphi,Delphi Xe2,在Delphi XE2 Win32平台中运行以下代码可以正常工作。但是,如果在调试模式下运行,在win64平台中编译的相同代码将导致“EnumRCDataProc”中的访问冲突: procedure TForm2.Button1Click(Sender: TObject); function EnumRCDataProc(hModule: THandle; lpszType, lpszName: PChar; lParam: NativeInt): Boolean; stdcal

在Delphi XE2 Win32平台中运行以下代码可以正常工作。但是,如果在调试模式下运行,在win64平台中编译的相同代码将导致“EnumRCDataProc”中的访问冲突:

procedure TForm2.Button1Click(Sender: TObject);
  function EnumRCDataProc(hModule: THandle; lpszType, lpszName: PChar; lParam:
      NativeInt): Boolean; stdcall;
  begin
    TStrings(lParam).Add(lpszName);
    Result := True;
  end;

var k: NativeInt;
    L: TStringList;
    H: THandle;
begin
  H := LoadPackage('resource.bpl');
  L := TStringList.Create;
  try
    EnumResourceNames(H, RT_RCDATA, @EnumRCDataProc, NativeInt(L));
    ShowMessage(L.Text);
  finally
    L.Free;
    UnloadPackage(H);
  end;
end;

在Win64平台上调试Delphi XE2 IDE中的代码时,我发现EnumRCDataProc中hModule的值与变量H不匹配。我怀疑我为EnumRCDataProc构造的参数可能有问题。然而,我不知道怎么做。有什么想法吗?

问题是您已经将
EnumRCDataProc
设置为本地过程。您需要将其移到方法之外

function EnumRCDataProc(hModule: HMODULE; lpszType, lpszName: PChar; lParam:
    NativeInt): BOOL; stdcall;
begin
  TStrings(lParam).Add(lpszName);
  Result := True;
end;

procedure TForm2.Button1Click(Sender: TObject);
var k: NativeInt;
    L: TStringList;
    H: HMODULE;
begin
  H := LoadPackage('resource.bpl');
  L := TStringList.Create;
  try
    EnumResourceNames(H, RT_RCDATA, @EnumRCDataProc, NativeInt(L));
    ShowMessage(L.Text);
  finally
    L.Free;
    UnloadPackage(H);
  end;
end;
在第一次检查时,我预计编译器会对您的代码发出错误:

E2094分配给过程变量的本地过程/函数“回调”

但事实并非如此。我深入研究了一下,发现
EnumResourceNames
的回调参数被声明为type
Pointer
。如果标头转换已将其声明为类型化回调参数,则上述错误消息确实已发出。在我看来,标题翻译在这方面很差。放弃类型系统的安全性似乎没有什么好处

您的代码在32位代码中工作这一事实只是一个幸运的巧合,它依赖于实现细节。您的运气在64位上耗尽。同样,如果启用了类型检查系统,编译器可能会立即告诉您出了什么问题

其他一些评论:

  • EnumRCDataProc
    的声明中有几个不正确的类型:
    hModule
    应该是
    hModule
    类型,函数结果应该是
    BOOL
  • LoadPackage
    是获取模块句柄的一种相当重要的方法。我更喜欢使用
    LOAD\u LIBRARY\u AS\u DATAFILE\u EXCLUSIVE
    LOAD\u LIBRARY\u AS\u IMAGE\u资源
    选项查看

  • 编译器不应该对此抱怨。本地枚举函数根本不是问题。只要回调不访问任何表单,就不会有问题。同样,仅出于此目的,将回调取出才是正确的做法。过度罢工是什么意思?这是否使上述答案无效?我认为E2094应该是一个警告,而不是一个错误。我有权访问外部的任何东西并使程序崩溃。毕竟,我可能不会那样做,然后什么也不会崩溃。另一方面,即使它被声明为过程类型,我也可以通过类型转换绕过它,例如像
    TEnumRCDataProc(指针(@EnumRCDataProc))
    一样传递它。我看不出“2094”是一个编译错误的意义。@Sertac error在64位上看起来非常好。本地功能非常特殊,很难实现。特别是当存在递归时。在我看来,这个错误是可以避免的,因为你总是可以通过强制转换来避免它。@DavidHeffernan在32位Delphi中,如果本地函数不使用周围函数中的任何内容,编译器会使它成为一个“实”函数。在64位中,编译器始终插入隐藏的基指针参数。这当然是一个实现细节,但他们可以很容易地决定不破坏现有代码。但我猜他们在实现64位编译器后端时并没有考虑/知道这个问题。