【问题标题】:Is globalalloc with GMEM_MOVEABLE dangerous for local variables in Delphi?带有 GMEM_MOVEABLE 的 globalalloc 对 Delphi 中的局部变量是否危险?
【发布时间】:2013-10-29 18:28:55
【问题描述】:

我们的编程部门刚刚花了一个非神话般的人工月来追踪我们认为是第 3 方组件中的错误,这是他们受版权保护的源代码:

function TGDIPPicture.GetImageSizes: boolean;
var
  multi: TGPImage;
  pstm: IStream;
  hGlobal: THandle;
  pcbWrite: Longint;

begin
  result := false;

  if Empty then
    Exit;

  if FDataStream.Size = 0 then
    Exit;

  hGlobal := GlobalAlloc(GMEM_MOVEABLE, FDataStream.Size);
  if (hGlobal = 0) then
    raise Exception.Create('Could not allocate memory for image');


  try
    pstm := nil;
    // Create IStream* from global memory
    CreateStreamOnHGlobal(hGlobal, TRUE, pstm);
    pstm.Write(FDataStream.Memory, FDataStream.Size,@pcbWrite);
    multi := TGPImage.Create(pstm);

    FWidth := multi.GetWidth;
    FHeight := multi.GetHeight;

    Result := true;

    multi.Free;
  finally
    GlobalFree(hGlobal);
  end;

end;

我们发现问题出在 TMS 的 AdvOfficeTabSet 上。如果我们添加标签,那么它会崩溃,如果我们不添加标签,那么它不会崩溃。 (崩溃是那些无法调试的应用程序挂起之一,在真正的问题发生后 10 步会影响您)。

Raymond Chen's advice 之后,我用 GPTR 替换了 GMEM_MOVEABLE,它似乎已经解决了问题。

我想知道是否有人可以告诉我上述代码是否有任何使用 GMEM_MOVEABLE 的正当理由。 AFAIK 仅适用于剪贴板,应始终与 GlobalAlloc 一起使用。


当我键入这个时,另一个程序员使用我的代码在 GlobalFree 函数中遇到了错误。所以,显然这也不起作用。真的可以在这里使用一些帮助!

*CreateStreamOnHGlobal 是一个 Windows API 函数。 (显然是prefers GMEM_MOVEABLE

*TGPImage 是 TMS 的 GDI+ 库实现的一部分。

【问题讨论】:

  • 您将TRUE 传递给CreateStreamOnHGlobal() 以获取fDeleteOnRelease 参数,这意味着流现在拥有内存句柄,您不应自己调用GlobalFree()。您似乎也没有释放流(除非 Delphi 自动为您这样做,或者 TGPImage.Create 这样做?如果是这样,您将肯定最终获得双重释放内存句柄)。我不知道这些事情是否会导致您的崩溃,但在这种情况下使用 GMEM_MOVEABLE 并没有固有的问题,所以我很确定这不是问题。
  • 这是真正的代码吗?它有很多问题。不分配结果值。缺少错误检查。等等。我们如何确定 GlobalAlloc 甚至是问题所在。
  • 谢谢,这就是我们第三次尝试使用我们根本不想使用的功能的尝试!我尝试在 CreateStreamOnHGlobal 中将 TRUE 更改为 false 并遇到不同的问题,一些 realloc 的事情,希望你所说的是正确且唯一的答案。
  • @DavidHeffernan 是的,它来自 TMS,我想我通过发布此内容侵犯了某些版权,但它应该被修复,而且它不够新颖,不足以关心版权。如果可能的话,我宁愿不使用全局变量——对我来说改变不会太多。
  • 很难相信 TMS 这么写的。这是牛肚。

标签: delphi winapi memory gdi+ tms


【解决方案1】:

Jonathan 发现了一个明显的问题,即 HGLOBAL 的双重释放。但正如您所发现的,使用 GMEM_MOVEABLE 是正确的。

坦率地说,代码看起来不必要地复杂。我建议您使用内置的流适配器并避免使用任何 GlobalAlloc。要获得 IStream,您只需要这样做:

pstm := TStreamAdapter.Create(FDataStream);

就是这样。

【讨论】:

  • 嗯,编译好了!我的标签上出现了一个看起来很奇怪的关闭按钮。在我从 TMS 获得的所有包中,大约有 20 个与上述类似的功能,其中两个似乎具有正确的(非双重释放)实现,也许我会不理会它们。
猜你喜欢
  • 1970-01-01
  • 2011-07-28
  • 2015-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多