【问题标题】:Adding large resources with UpdateResource使用 UpdateResource 添加大型资源
【发布时间】:2014-09-09 15:09:01
【问题描述】:

在 Windows 文档中,我没有看到对可以使用 UpdateResource 添加的资源的大小限制的引用,但我似乎偶然发现了一个 - 而且它很小!

我正在开发一个 Windows 功能区应用程序,并希望以编程方式构建和附加资源。使用 $R 指令链接资源很有效,但是当我从代码中附加相同的东西时,我不断地得到内存垃圾。

我已经设法使用字符串资源将其简化为一个简单的示例:

  Handle := BeginUpdateResource(PChar(DestFileName), True);
  try
    AddResource(Handle, 'STRING', 'ManyXs', StrUtils.DupeString('X', 1000));
  finally
    EndUpdateResource(Handle, False);
  end;

而AddResource定义为:

procedure TForm2.AddResource(Handle: NativeUInt; ResType, ResName, Value: string);
begin
  if not UpdateResource(Handle, PChar(ResType), PChar(ResName), 1033,
    PChar(Value), Value.Length * SizeOf(Char)) then
    RaiseLastOSError;
end;

请暂时忽略我的硬编码语言。

当我在调用它之后检查资源时,我看到了一千个 X。太棒了。

我可以将资源更改为 1990 Xs,这很好。到 1991 年的那一刻,我把废话写到 DLL 中。资源的大小正确指示为 3982(1991 * 2,因为它是 Unicode),但内容只是内存中的内容转储。

我可以使用我的资源编辑器查看更大的资源,并且 IDE 会定期插入更大的资源(例如 Delphi 表单),所以我肯定遗漏了一些东西。

我尝试了以下方法,尽管不认为它们会有所作为(他们没有):

  1. 仅使用大内存缓冲区而不是字符串
  2. 使用 Ansi 版本的 UpdateResource 函数
  3. 许多不同的资源类型 - 我真正需要的是 UIFILE
  4. 在 API 中寻找其他函数(我没有找到)
  5. 1、2 和 3 的组合

有什么想法吗?

更新: 受到 cmets 和 Jolyon 回答的启发,又尝试了一些东西。

首先,我也在 Delphi XE7 和 XE5 中尝试过(原来是在 XE6 中)。我不再安装 XE2,所以我无法确认 Sertak 所说的内容。我会看看我办公室的其他人是否还安装了它。

其次,这里是内存缓冲区版本:

procedure TForm2.AddResource(Handle: NativeUInt; const ResType, ResName, Value: string);
var
  Buffer: Pointer;
  BuffLen: Integer;
begin
  BuffLen := Value.Length * SizeOf(Char);
  GetMem(Buffer, BuffLen);
  try
    StrPCopy(PChar(Buffer), Value);

    if not UpdateResource(Handle, PChar(ResType), PChar(ResName), 1033,
      Buffer, BuffLen) then
      RaiseLastOSError;
  finally
    FreeMem(Buffer);
  end;
end;

我实际上有此代码的先前版本,在调用 UpdateResource 之前我将该指针的内容转储到一个文件中,并且文件正确保存,但资源仍然保存垃圾。然后我做了这个版本,完全不涉及字符串:

procedure TForm2.AddResource(Handle: NativeUInt; const ResType, ResName: string; 
  C: AnsiChar; Len: Integer );
var
  Buffer: Pointer;
  BuffLen: Integer;
begin
  BuffLen := Len;
  GetMem(Buffer, BuffLen);
  try
    FillMemory(Buffer, Len, Byte(C));

    if not UpdateResource(Handle, PChar(ResType), PChar(ResName), 1033,
      Buffer, BuffLen) then
      RaiseLastOSError;
  finally
    FreeMem(Buffer);
  end;
end;

在这个版本中,当我使用 3882 Xs 时,我仍然遇到同样的问题。当然,我现在使用的是单字节字符,这就是为什么它是双字节的。但我有完全相同的问题。

我确实注意到 TDUMP 输出中的版本之间存在差异。对于版本 1(字符串)和 2(复制到缓冲区的字符串),当我使用 1991 个字符时,我的资源大小突然显示为 FFFFFF90。对于版本 3(无字符串),大小是我使用的任何大小的实际十六进制值。

【问题讨论】:

  • 请记住我对 Delphi 知之甚少,string 是一个“窄”字符串吗? UpdateResource 的文档指出 lpData 不能指向 ANSI 数据,它必须是 Unicode。所以也许使用WideString 可能是一个想法?查看here 表明String 默认为AnsiString
  • @icabod 在现代 Delphi 中 string 是 UTF-16 UnicodeString
  • @DavidHeffernan:有一个现代德尔福!?!
  • @icabod 有。听起来你落后于时代了。使用古老的德尔福基础知识网站也是一个大错误。 Delphi 文档齐全,文档在线。请跟上! ;-)
  • 无法复制,用 XE2 测试了 50000 次重复。

标签: delphi winapi resources


【解决方案1】:

您得到的是“垃圾”数据,但数据的正确 大小 让我怀疑字符串值的 PChar() 转换会产生不正确的地址.这通常不应该是一个问题,但我想知道问题是否是由于将函数的结果直接传递给方法的参数而导致的一些奇怪行为?一种出于某种奇怪原因的行为,仅在涉及的字符串达到一定大小时才会触发,这可能表明某些极端情况的优化行为。

如果它是某些特定版本的 Delphi 中优化(和/或其他编译器设置)的某种组合,这也可能解释重现问题的困难。

我建议尝试通过在显式变量中创建新资源字符串并将其传递给 AddResource() 方法来消除这种可能性。我还建议您在参数语义中明确,并且由于所涉及的字符串没有被修改,也不打算被修改,在 AddResource() 方法中,将其声明为正式的 const 参数。

您确实提到尝试过使用“内存缓冲区”的替代方法。如果上述建议不能解决问题,那么发布一个使用这些建议重现问题的最小示例可能会有所帮助,以消除更奇特的“字符串”类型对事物的任何可能影响。

【讨论】:

  • 这听起来更像是一条评论。当然,我知道没有这样的错误。我们现在应该已经注意到了。
  • 你会惊讶于错误可以被忽视多久。我可以想到至少有两个在 Delphi 中运行了很长时间,然后才在野外出现(并且在修复之前更长时间)。我也建议进一步简化,。也许将其视为抵消所有那些应该是答案的 cmets :) 。 (我确实犹豫是否将其发布为答案,但 cmets 还不够大)
猜你喜欢
  • 2017-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多