任务栏内存泄漏

7

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 中。这真是个震惊! - David Heffernan
我认为处理这个问题不会很容易。修复问题可能是前进的道路。但如果没有重现,我就无法再做更多了。你需要向emba提交一个错误报告。创建异常,然后却未能引发它?!!! - David Heffernan
重现项目在此处:http://www.filedropper.com/repro_1 - Gabriel
2
@Altar:在窗体上放置一个 TTaskbar 组件,并调用 Taskbar1.UpdateTab() 即可重现此问题。 - Günther the Beautiful
我认为你之前的标题对于未来读者在搜索结果中更有用,而你现在使用的标题则不够具体。 - Ken White
显示剩余3条评论
1个回答

10

这段代码中存在内存泄漏问题,涉及到 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。我建议您提交一个错误报告。


另一种解决方法是尝试避免执行虚假的行。我认为如果您从.df文件中删除此行,您应该可以避免虚假的行,从而避免泄漏:

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
2
内存泄漏问题已于去年向Embarcadero报告,修复程序已于上个月检查完成,因此应该会在下一个Delphi版本中得到解决:QC#128865 TTaskbarBase创建异常但不引发它们 - Remy Lebeau
1
感谢David的快速回答和解决方案,也感谢Remy找到了错误报告。|||我希望下一个版本的Delphi会非常好,非常棒。因为我真的买不起他们每年推出的所有版本。而且只购买昂贵的产品来获取一些错误修复肯定会留下更加苦涩的味道。我想继续编写我的工具,而不是把头撞在这些错误上——最近这种情况发生得有点太频繁了。 - Gabriel
从QC中,我无法真正理解这个问题在哪个版本的Delphi中得到了修复。你能知道吗? - Gad D Lord
@GadDLord 我们期待它在即将到来的XE8中出现。 - David Heffernan
显示剩余3条评论

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