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

5
在 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构建的参数有问题。但是,我想不出具体的解决办法。你有什么想法吗?

1个回答

5
问题在于您将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 本地过程/函数“Callback”分配给了过程变量。但它并没有这样做。我深入挖掘后发现,EnumResourceNames 的回调参数被声明为 Pointer 类型。如果头文件翻译将其声明为类型化的回调参数,则确实会产生上述错误消息。在我看来,头文件的翻译在这方面很差。放弃类型系统的安全性似乎没有什么好处。 您的代码在32位代码中可以工作只是依赖于具体实现细节的幸运巧合。在64位上您的运气就用尽了。同样,如果启用了类型检查系统,则编译器可以立即告诉您错误所在。其他一些评论: 1. “EnumRCDataProc”在其声明中有一些不正确的类型: “hModule”应为“HMODULE”类型,函数结果应为“BOOL”类型。 2. “LoadPackage”是获取模块句柄的一种相当重量级的方法。我更喜欢使用带有“LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE”和“LOAD_LIBRARY_AS_IMAGE_RESOURCE”选项的“LoadLibraryEx”

编译器不应该抱怨这个。本地枚举函数根本不是问题。 - OnTheFly
1
只要回调函数不访问任何不应该访问的表单内容,那就不会有问题。但是为了这个目的,将回调函数移除是正确的做法。什么是over-strike的意思?那是否使上面的答案无效? - Sertac Akyuz
1
在我看来,E2094 应该是一个警告而不是错误。我应该有权访问任何外部内容并使程序崩溃。毕竟,我可能不会这样做,那么什么也不会崩溃。另外,即使它被声明为过程类型,我也可以通过类型转换来绕过它,例如像 TEnumRCDataProc(Pointer(@EnumRCDataProc)) 这样传递它。我无法理解 '2094' 作为编译错误的意义所在。 - Sertac Akyuz
1
@Sertac 在64位系统上看起来选择错误很好。本地函数非常特殊且具有挑战性,尤其是当存在递归时。在我看来,出现错误问题是可以接受的,因为你总是可以进行强制转换来避免它。 - David Heffernan
4
在32位Delphi中,如果局部函数不使用周围函数的任何内容,编译器会将其作为“真正”的函数处理。而在64位中,编译器总是插入隐藏的基指针参数。这当然是一个实现细节,但他们本可以很容易地决定不破坏现有的代码。但我想当他们实现64位编译器后可能没有考虑到这个问题。 - Andreas Hausladen
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接