【问题标题】:EnumResourceNames returns Windows error 998 (Invalid access to memory location)EnumResourceNames 返回 Windows 错误 998(对内存位置的访问无效)
【发布时间】:2014-04-11 23:55:06
【问题描述】:

(见底部的编辑)

使用 EnumResourceNames,我试图在我自己的 dll 中查找存储为资源的图标。这是整个dll代码:

library focusRes;

{$R focusResResource.res} // contains the icons I need to load
{$R *.res}

begin
end.

我可以使用 LoadLibrary 和 LoadLibraryEx(已验证)正确加载 dll。但是对 EnumResourceNames 的调用返回 false,GetLastError 返回 998(对内存位置的访问无效)。调用代码是:

hdll := LoadLibraryEx( PChar( DLLFilename ), 0, LOAD_LIBRARY_AS_DATAFILE );
// OR: hdll := LoadLibrary( PChar( DLLFilename ));
enumResult := EnumResourceNames( hDll, RT_ICON, @EnumResFlags, 0 );
// (hDll is the handle returned from LoadLibrary)

还有回调函数:

function EnumResFlags( hDll : HMODULE; ResType, ResName : PChar; 
   notUsed : pointer ) : integer; stdcall;
begin
  // NEVER GETS CALLED
  // log( ResName);
  result := 1; // continue enumeration
end;

回调是一个独立的函数(不是对象方法或本地函数)。

在我的调查中,我发现了一些令人困惑的线索:

  • 问题似乎出在我的 dll 上,因为如果我替换为随机 从第三方应用程序获取 dll,问题就消失了。

  • 问题似乎不是出在我的 dll 上,因为我可以打开它 在第三方图标编辑器中,dll中存储的所有图标都是 正确加载。

  • 当我尝试为特定图标名称创建资源流时, 例外是“找不到资源 [名称]”。 (但是图标编辑器觉得没问题)

(Windows 7 32 位上的 Delphi XE)

编辑 1: 创建一个新的 Delphi 项目并将以下行放入其中:

enumResult := EnumResourceNames( hInstance, RT_ICON, @EnumCallback, 0 );

结果:错误 998。将 RT_ICON 更改为其他内容,例如 RT_RTCDATA,问题就消失了。

编辑 2:如果我没有在回调中引用 ResName 参数,问题不会发生。 (如果我在回调中只说“结果:= 1”,则没有错误。)回到 Delphi 3(!)并得到相同的确切结果,所以它不是 XE 特有的。如果我尝试枚举 RT_RCDATA 而不是 RT_ICON,没有问题,我可以读取 ResName。

【问题讨论】:

  • 还发现:如果,作为一个实验,我用hInstance代替dll模块句柄,结果是一样的:错误998。(所以问题不在于如何加载dll)。
  • ...如果我传递零作为回调函数地址,错误仍然是998。
  • 您的 DLL 是否已经被您的应用程序以某种方式加载?
  • 没有。它在 Enum 调用之前直接加载。
  • 您能否发布资源脚本内容的示例,以及您“创建资源流”的尝试,包括与该资源脚本示例相关的实际代码?

标签: delphi dll


【解决方案1】:

您没有正确解析回调的lpszTypelpszName 参数。您失败的测试涉及传递给回调的资源 ID,而不是资源名称。当您尝试通过指针访问进程内存的前 64kb 时,这是一次无效的内存访问。

您需要测试ResType/ResName 是名称还是 ID,然后进行相应的处理,如下所示:

function EnumResFlags( hDll : HMODULE; ResType, ResName : PChar; notUsed : pointer ) : integer; stdcall;
begin
  if IS_INTRESOURCE(ResName) then
    log(IntToStr(Integer(ResName)))
  else
    log(ResName);
  ...
end;

如果你的Delphi版本没有定义IS_INTRESOURCE(),你可以手动定义:

function IS_INTRESOURCE(lpszType: PChar): BOOL;
begin
  Result := ULONG_PTR(lpszType) shr 16 = 0;
end;

【讨论】:

  • 使用if IS_INTRESOURCE(lpszName) then 而不是if (Integer(lpszName) shr 16) = 0 thenIS_INTRESOURCE() 在 Delphi 的 Windows 单元中。
  • @Remy - 我在 D2007 中找不到 IS_INTRESOURCE。显然后来的版本有它。谢谢!
  • 我刚查了一下,是在XE2中添加的。
  • 谢谢!我无法理解存储在 dll 中的任何图标如何使用 ID 而不是名称,直到我意识到 Delphi 编译器会自动插入它自己的“主图标”。可能是那个图标被传递给回调并触发了问题。
  • @mood - 不客气。是的,这似乎是合理的。 AFAICS,Delphi 的默认“MAINICON”图标组由一个未命名的图标组成。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-12
  • 1970-01-01
  • 1970-01-01
  • 2011-12-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多