0

I've found a strange memory leak in a project that I've been newly assigned to.

On terminating, the program displays the following FastMM4 error message.

FastMM4 Error

The project uses BusinessSkinForm

TbsaSpeedButtonSubClass is from the third party BusinessSkinForm library, however the speed buttons on that form appear to be regular VCL form controls.

When I add another speed button to the form,

speedButton

I now have 25 instances of TbsaSpeedButtonSubClass that leak instead of 24.

Which leads me to think that the leak is due to the TSpeedButton. However, this seems strange to me since I would expect form components to be automatically freed by the form upon destruction.

Perhaps BusinessSkinForm does something unusual to the form which results in a leak...

I'm not sure how to get rid of this leak

EDIT

Thanks to KenWhite I have a memory leak report from FastMM4

Here it is on Pastebin

EDIT

As shown in the stack trace, the problem can be traced to a TMUSICMainForm.SkinForm_OnCreate(SkinForm: TForm);

The problem seems to be related to a BSA: TbsaSkinAdapter

If I comment out the line BSA.ChangeSkinData;

the leak is no longer present.

EDIT

Here is the important part of the stack trace

--------------------------------2015/11/24 12:16:03-------------------------------- A memory block has been leaked. The size is: 308

This block was allocated by thread 0x1258, and the stack trace (return addresses) at the time was: 402AB6 [madZip][madZip][@GetMem] 4035F9 [madCrypt][madCrypt][TObject.NewInstance] 4039CA [madCrypt][madCrypt][@ClassCreate] 67438A [bsaadapter.pas][bsaadapter][TbsaSpeedButtonSubclass.Create][11537] 66137E [bsaadapter.pas][bsaadapter][TbsaHook.SetControl][2637] 403A1E [madCrypt][madCrypt][@AfterConstruction] 665BFB [bsaadapter.pas][bsaadapter][TbsaSkinManager.DoControlMessage][4898] 6615B7 [bsaadapter.pas][bsaadapter][TbsaHookCollection.AddControl][2760] 404ACB [madExcept][madExcept][@LStrSetLength] 662A0E [bsaadapter.pas][bsaadapter][TbsaSkinManager.CollectSpeedButton][3544] 92C81D [Sources\uMainForm.pas][uMainForm][TMUSICMainForm.SkinForm_OnCreate][4778]

The block is currently used for an object of class: TbsaSpeedButtonSubclass

The allocation number is: 475863

Current memory dump of 256 bytes starting at pointer address 7E8A7670: 64 C9 65 00 00 00 00 00 00 00 00 00 00 00 00 00 68 F3 48 00 50 96 97 7E B8 5E 74 7E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 B8 D1 74 7E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 d É e . . . . . . . . . . . . . h ó H . P – — ~ ¸ ^ t ~ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ¸ Ñ t ~ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

EDIT

I've managed to create a minimum working example of the memory leak.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  BusinessSkinForm, bsaadapter, Buttons
  ;

type
  TForm1 = class(TForm)
    SpeedButton1: TSpeedButton;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
var
  BSF: TbsBusinessSkinForm;
  BSA: TbsaSkinAdapter;
begin
    BSF := TbsBusinessSkinForm.Create(Self);

    BSF.BorderIcons:=[biMinimize,biMaximize];

    BSA := TbsaSkinAdapter.Create(Self);

    BSA.AdapterType := bsaUseClasses;

    BSA.ChangeSkinData;
end;

end.

I've found that even if I comment out seemingly innocent looking lines like BSF.BorderIcons:=[biMinimize,biMaximize]; or BSA.AdapterType := bsaUseClasses; then the memory leak disappears.

Note my delphi form in this example contains 1 TSpeedButton

minimal bug

EDIT

I should also add that I'm using windows 7 (64bit), 6GB ram, delphi 5, Business Skin version 4.70

EDIT

the bsaadapter unit in BusinessSkinForm contains a function

procedure TbsaSkinManager.DoUnhook(Control: TControl; Handle: HWnd);
var
  i: integer;
  SC: TbsaSubclass;
  R: TRect;
begin
  if FUnhooking then Exit;

  if FUnhookedList = nil then
  begin
    FUnhooking := true;
    Exit;
  end;

  FUnhooking := true;
  try
    for i := FHandleList.Count - 1 downto 0 do
    begin
      SC := TbsaSubclass(FHandleList[i]);

      if (Handle <> 0) and (SC.Handle = Handle) then
      begin
        R := Rect(0, 0, 2000, 2000);
        PostMessage(Handle, WM_NCPAINT, 0, 0);
        InvalidateRect(Handle, @R, false);
        FHandleList.Delete(i);
        SC.Free;
      end;

      if (Control <> nil) and (SC.Control = Control) then
      begin
        FHandleList.Delete(i);
        SC.FControl := nil;
        if not (Control is TGraphicControl) then
          SC.Free;
      end;
    end;
  finally
    FUnhooking := false;
  end;
end;

It seems that speed buttons do not get freed because they are instances of TGraphicControl

if not (Control is TGraphicControl) then SC.Free;

While a regular TButton will get freed

sav
  • 2,064
  • 5
  • 25
  • 45
  • You can start by following the instructions in the *Note* in the dialog you posted, which will lead you to specific code. See [this answer](http://stackoverflow.com/a/1130506/62576) for details. – Ken White Nov 24 '15 at 00:42
  • I added `{$DEFINE FullDebugMode}` to my `dpr` file. No output file was produced. – sav Nov 24 '15 at 00:52
  • There's more to it than that - did you read the answer I linked above? – Ken White Nov 24 '15 at 00:53
  • @KenWhite Yep, `FastMM4Options.inc` included, FastMM4 is first in uses, set Map file to Detailed, FastMM_FullDebugMode.dll is in directory – sav Nov 24 '15 at 01:10
  • 1
    Have you done a full build, rather than a compile? – Gerry Coll Nov 24 '15 at 01:12
  • 1
    Did you also edit the `FastMMOptions.inc` file to set the appropriate defines? Did you build your project (not *compile*)? – Ken White Nov 24 '15 at 01:12
  • @KenWhite I think rebuilding it did the trick – sav Nov 24 '15 at 01:16
  • 1
    So look at the stack trace. The numbers in `[]` toward the right side indicate line numbers. You can see exactly where the class in question is used, which indicates that `TSpeedButton` has been affected by the skin library code (looks like it's in `bsadapter.pas`, from a quick glance). Use the source to figure out where the leak is occurring. FastMM is telling you *exactly* where to look. – Ken White Nov 24 '15 at 01:23
  • You should include your memory leak report directly in your question, not in an off-site link which might not be valid a year from now. – Jerry Dodge Nov 24 '15 at 01:44
  • I've tried this using the `TButton` and it does not leak, it only leaks when using `TSpeedButton`. I wonder if this indicates a leak/bug in the BusinessSkinForm library. – sav Nov 24 '15 at 05:44
  • There's no code here. There's a defect somewhere. Look in the code. – David Heffernan Nov 24 '15 at 06:44
  • Yea, the whole code base would be too big to post, but I think the important part of it is the call to ChangeSkinData since removing this line of code eliminates the leak. I think the problem is related to how BusinessSkinForm uses the TSpeedButton control. – sav Nov 24 '15 at 10:52
  • So look in the code. You know which class is being leaked. Work out why the code creates instances of that class but does not destroy them. – David Heffernan Nov 24 '15 at 13:30
  • Your continued edits are not helping, because you're simply repeating the same information. I've already pointed out that the TSpeedButton is being modified by code in `TbsaSpeedButtonSubclass`, so any change you make to the property of a `TSpeedButton` is meaningless - the functionality in the subclass is affecting the behavior. David and I have both told you **precisely** what you need to do to figure out what the issue is, and none of your edits address doing so. We don't have the relevant component in order to debug it for you - **you** need to **debug the code** based on the stack trace. – Ken White Nov 25 '15 at 00:02

1 Answers1

1

From what I can see here there are a number of ways which I can proceed from here.

  1. Submit a fix to BusinessSkinForm to handle the speed button

  2. Live with using a work around ie: BSA.AdapterType := bsaUseNames; instead of BSA.AdapterType := bsaUseClasses;

  3. Replace the speed buttons with the regular TButton.

sav
  • 2,064
  • 5
  • 25
  • 45
  • I'll leave this one open for a while in case anyone else has some other suggestions. Thanks for all the help :) – sav Nov 25 '15 at 02:07