7

Embarcadero's TTaskbar has a memory leak. Since I dropped this control on my form, FastMM reports a leak every time I close the app.

I tried to mute FastMM with this code:

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

but it won't work. How to register this leak?


A memory block has been leaked. The size is: 100

This block was allocated by thread 0xC64, and the stack trace (return addresses) at the time was: 406A52 409A7B 409CAC 4283A0

[System.SysUtils][System][System.SysUtils.FmtStr] 409CC6 40D775 7628A65F
[Unknown function at StretchDIBits] 7731594E
[Unknown function at RtlpNtMakeTemporaryKey] 7731594E
[Unknown function at RtlpNtMakeTemporaryKey] 773168F8
[Unknown function at RtlpNtMakeTemporaryKey] 773168DC
[Unknown function at RtlpNtMakeTemporaryKey]

The block is currently used for an object of class: UnicodeString
The allocation number is: 2209

A memory block has been leaked. The size is: 36

This block was allocated by thread 0xC64, and the stack trace (return addresses) at the time was: 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]

The block is currently used for an object of class: ETaskbarException
The allocation number is: 2207

This application has leaked memory. The small block leaks are (excluding expected leaks registered by pointer):

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

Ken White
  • 123,280
  • 14
  • 225
  • 444
Gabriel
  • 20,797
  • 27
  • 159
  • 293
  • I cannot reproduce here. OK now I can see where the leak is. It's in `TTaskbarBase.UpdateTab`. It's a shocker too! – David Heffernan Feb 19 '15 at 11:02
  • I don't think it's going to be easy to deal with this. Fixing the problem is probably the way forward. I cannot do any more without a repro though. You do need to submit a bug report to emba though. Creating an exception but then failing to raise it?!!! – David Heffernan Feb 19 '15 at 11:13
  • Repro project here: http://www.filedropper.com/repro_1 – Gabriel Feb 19 '15 at 11:28
  • 2
    @Altar: Dropping a `TTaskbar` component on a form and calling `Taskbar1.UpdateTab()` is sufficient for reproducing this – Günther the Beautiful Feb 19 '15 at 11:32
  • @GünthertheBeautiful-In my case is enough only to drop the component on the form! – Gabriel Feb 19 '15 at 11:36
  • @Altar That's because you form is, it turns out, a little special...... – David Heffernan Feb 19 '15 at 12:36
  • I think your previous title was much more useful in a search result for future readers here than the one you've just used, which is far less specific. – Ken White Feb 19 '15 at 21:06
  • @KenWhite-Hi Ken. You are right but the 'direction' of the question changed since I asked it. I have updated the question title one more time. I think now is accurate (regarding what we discussed here) and also useful for those that are searching related to TTaskBar – Gabriel Feb 19 '15 at 21:55

1 Answers1

10

The memory is leaked in this code from 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]);
....

The final line creates an exception, and then does nothing with it. The exception and the string that it owns are leaked. As reported by FastMM.

You can register these objects as being leaked if you can obtain their addresses. However, you cannot do that. There is no way to refer to this exception object.

If you simply must avoid this mis-reported leak, and it makes sense that you would, then you'll need to include a fixed version of System.Win.TaskbarCore in your project. Make a copy of that file, and add it to your project. Then modify the code to fix the fault. My guess is that it would go like this:

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;   

Clearly this needs to be reported to Embarcadero. I suggest that you submit a bug report.


Another way around this is to try to avoid the bogus line executing at all. I believe that if you remove this line from your .dfm file, you should avoid the bogus line, and therefore avoid the leak:

Visible = True

Simply remove that line, it seems to be the trigger.

Note that I worked this out by cutting the project down to its bare bones. In order to reproduce the problem this is the minimal dfm file needed:

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

And with this dfm file there is not leak:

object Form1: TMainForm
  object Taskbar1: TTaskbar
  end
end

By cutting the project down to the bare minimum, I was able to find the trigger. I cannot stress enough how valuable this technique of minimising a reproduction is.


Thanks to Remy for finding the QC report for this fault: QC#128865

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • They obviously missed to `raise` that exception. – TLama Feb 19 '15 at 12:40
  • @TLama Well, not quite. If `FRegistered` is `True` then it should not be raised. I'll add an update as to what I think the fix might be. – David Heffernan Feb 19 '15 at 12:48
  • I've addressed just the leak issue, which is clearly an exception that was supposed to be raised. – TLama Feb 19 '15 at 12:51
  • @TLama Yes, but if it was raised, then the library would not work at all. Raising it would avoid the leak. As would not creating the exception in the first place. And in fact I believe that we need a bit of both in the solution. Raise it when there really is an error, and not because we are already registered, but do not even create the exception when we are registered. – David Heffernan Feb 19 '15 at 12:52
  • 2
    The memory leak was reported to Embarcadero last year, and a fix was checked in last month, so it should be in the next Delphi version: [QC #128865 TTaskbarBase creates exceptions but doesn't raise them](http://qc.embarcadero.com/wc/qcmain.aspx?d=128865) – Remy Lebeau Feb 19 '15 at 17:53
  • 1
    Thanks David for your quick answer and solution and thanks Remy for locating the bug report. ||| I hope next version of Delphi will be outstandingly good and nice. Because I don't really afford to purchase all those versions they pour every year. And purchasing an expensive product only to get some bug fixes certainly leaves a even bitter taste. I want to keep programming my tools, not smashing my head into those bugs - and lately it happen a bit to often. – Gabriel Feb 19 '15 at 20:57
  • From the QC I cannot really understand in which version of Delphi this was fixed. Can you? – Gad D Lord Mar 16 '15 at 20:20
  • @GadDLord We are expecting it to be in the upcoming XE8 – David Heffernan Mar 16 '15 at 20:29