【问题标题】:TTaskBar memory leakTTaskBar 内存泄漏
【发布时间】:2015-04-20 16:40:26
【问题描述】:

Embarcadero 的 TTaskbar 存在内存泄漏。由于我在表单上删除了此控件,因此每次关闭应用程序时 FastMM 都会报告泄漏。

我尝试使用以下代码使 FastMM 静音:

procedure TMainForm.FormCreate(Sender: TObject);
begin
 fastmm4.RegisterExpectedMemoryLeak(Taskbar);
end;

但它不会工作。如何注册此泄漏?


内存块已泄漏。大小为:100

这个块是由线程 0xC64 分配的,堆栈跟踪(返回 地址)当时是:406A52 409A7B 409CAC 4283A0

[System.SysUtils][System][System.SysUtils.FmtStr] 409CC6 40D775 7628A65F
[StretchDIBits 处的未知函数] 7731594E
[RtlpNtMakeTemporaryKey 处的未知函数] 7731594E
[RtlpNtMakeTemporaryKey 处的未知函数] 773168F8
[RtlpNtMakeTemporaryKey 处的未知函数] 773168DC
[RtlpNtMakeTemporaryKey 的未知函数]

该块当前用于类对象:UnicodeString
分配号为:2209

内存块已泄漏。尺寸为:36

这个块是由线程 0xC64 分配的,堆栈跟踪(返回 地址)当时是:406A52 407D43 40846A 42CD40
[System.SysUtils][System][System.SysUtils.Exception.CreateFmt] 5DEDD7
[System.Win.TaskbarCore][System.Win][System.Win.TaskbarCore.TTaskbarBase.UpdateTab] 610F00
[Vcl.Taskbar][Vcl][Vcl.Taskbar.CheckMDI] 5DF39F
[System.Win.TaskbarCore][System.Win][System.Win.TaskbarCore.TTaskbarBase.ApplyTabsChanges] 610DB8
[Vcl.Taskbar][Vcl][Vcl.Taskbar.TCustomTaskbar.Initialize] 5EB044
[Vcl.Forms][Vcl][Vcl.Forms.TApplication.Run] 62573A
[MinimalTemplate.dpr][MinimalTemplate][MinimalTemplate.MinimalTemplate][26]

该块当前用于类对象:ETaskbarException
分配号为:2207

此应用程序已泄漏内存。小块泄漏是(不包括指针注册的预期泄漏):

21 - 36 字节:ETaskbarException x 1
85 - 100 字节:UnicodeString x 1
[Vcl.Forms][Vcl][Vcl.Forms.TCustomForm.SetVisible] 5F5010

【问题讨论】:

  • 这里无法重现。好的,现在我可以看到泄漏的位置。它位于TTaskbarBase.UpdateTab。也太震撼了吧!
  • 我认为处理这个问题并不容易。解决问题可能是前进的方向。不过,如果没有复制品,我将无法再做任何事情。不过,您确实需要向 emba 提交错误报告。创建异常但未能引发异常?!!!
  • 在此处复制项目:filedropper.com/repro_1
  • @Altar:在表单上删除TTaskbar 组件并调用Taskbar1.UpdateTab() 足以重现此
  • @GünthertheBeautiful-在我的情况下,只需将组件放在表单上就足够了!

标签: delphi fastmm leakcheck


【解决方案1】:

这段代码从System.Win.TaskbarCore泄露了内存:

procedure TTaskbarBase.UpdateTab;
var
  LpfIsiconic: LONGBOOL;
  LHandle: HWND;
  LFlags: Integer;
begin
  if FTaskbarIsAvailable then
  begin
    LHandle := GetFormHandle;
    if not FRegistered and TaskBar.RegisterTab(LHandle) then
    begin
      TaskBar.SetTabOrder(LHandle);
      TaskBar.SetTabActive(LHandle);
      FRegistered := True;
    end
    else
      ETaskbarException.CreateFmt(SCouldNotRegisterTabException, [TaskBar.LastError]);
....

最后一行创建了一个异常,然后什么也不做。异常及其拥有的字符串被泄露。据 FastMM 报道。

如果您可以获得它们的地址,您可以将这些对象注册为被泄露。但是,您不能这样做。没有办法引用这个异常对象。

如果您只是必须避免这种错误报告的泄漏,并且这样做是有道理的,那么您需要在您的项目中包含一个固定版本的System.Win.TaskbarCore。制作该文件的副本,并将其添加到您的项目中。然后修改代码修复故障。我的猜测是它会是这样的:

if not FRegistered then
begin
  if TaskBar.RegisterTab(LHandle) then
  begin
    TaskBar.SetTabOrder(LHandle);
    TaskBar.SetTabActive(LHandle);
    FRegistered := True;
  end
  else
    raise ETaskbarException.CreateFmt(SCouldNotRegisterTabException, [TaskBar.LastError]);
end;   

显然,这需要向 Embarcadero 报告。我建议您提交错误报告。


解决此问题的另一种方法是尝试完全避免执行虚假行。我相信如果您从 .dfm 文件中删除此行,您应该避免使用虚假行,从而避免泄漏:

Visible = True

只需删除该行,它似乎是触发器。

请注意,我是通过将项目简化为基本原理来解决这个问题的。为了重现问题,这是所需的最小 dfm 文件:

object Form1: TMainForm
  Visible = True
  object Taskbar1: TTaskbar
  end
end

有了这个 dfm 文件,就没有泄漏:

object Form1: TMainForm
  object Taskbar1: TTaskbar
  end
end

通过将项目缩减到最低限度,我找到了触发点。这种最小化复制的技术的价值我怎么强调都不为过。


感谢 Remy 找到此故障的 QC 报告:QC#128865

【讨论】:

  • 他们显然错过了raise 那个例外。
  • @TLama 好吧,不完全是。如果FRegisteredTrue,那么它不应该被提升。我将添加一个关于我认为修复可能是什么的更新。
  • 我已经解决了泄漏问题,这显然是一个应该引发的异常。
  • 内存泄漏是去年报给Embarcadero的,上个月check in了一个fix,所以应该在下个Delphi版本:QC #128865 TTaskbarBase creates exceptions but doesn't raise them
  • 感谢 David 的快速回答和解决方案,并感谢 Remy 找到错误报告。 |||我希望下一个版本的 Delphi 会非常好。因为我真的买不起他们每年倒的所有版本。购买昂贵的产品只是为了得到一些错误修复肯定会留下更苦的味道。我想继续编写我的工具,而不是把我的头撞到那些错误上——最近它经常发生。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-10-08
  • 2013-01-20
  • 2011-10-31
  • 2019-08-10
  • 2013-06-24
  • 2011-03-22
  • 2012-09-21
相关资源
最近更新 更多