Jump to content
aehimself

TTreeNode leak when VCL styles are active

Recommended Posts

Hello all,

 

I was trying to get my head around this for a while but I simply can not find the solution. In my project I have a frame which I create runtime on a TTabSheet. On this frame I have 2 TTreeView components. 1 is on the frame itself, the second one is on an inner tabsheet:

 

image.thumb.png.d780d91d9793db104c89bfdcbe213fbb.png

 

Now, if the TreeView on the TabSheet has ANY items created if the application has a VCL style active, those items will not be freed up. I put a breakpoint in TTreeNode.Destroy and I can confirm that those are ONLY being called for nodes in the other TreeView.

 

I even put TreeView2.Items.Clear in the frame's destructor, but as Owner.HandleAllocated is false at that stage, deleting all nodes never happen. So I put a breakpoint in the frame destructor and TTreeNode.Destroy, and the thing I quickly realized is that the nodes of the first TreeView are being freed before the destructor, because of a window message (TVN_DELETEITEMA or TVN_DELETEITEMW). This is actually deleting the nodes in Vcl.ComCtrls.pas : 11910.

 

But, this message is sent only to the TreeView on the frame, not to the one in the PageControl. Again, only when VCL styles are active.

 

I will try to send this message manually in the destructor (or explicitly deleting all nodes...?) to see if it solves my issue, but it bugs me to hell that I don't know what is happening.

 

If I'd know where this message is sent, I could investigate why it is sent to / received by one TreeView only.

Anyone has any ideas?

 

 

Edit: Manually trying to delete nodes will fail, as TreeView.Items.Count shows 0 at the destructor already.

 

Edit-edit: I have an event on the frame itself which is being called BEFORE the .Free is called upon the owning tabseet. If I move TreeView2.Items.Clear in that handler, the memory leak disappears. I guess it's an other quirk of the VCL styles but I'd still like to know the reason...

Edited by aehimself
  • Like 1

Share this post


Link to post
1 minute ago, Dalija Prasnikar said:

Is there a bug report?

No, I didn't open a bug report because in my case it was just a demo of a component with on-the-fly style modification on the form itself to show how the component reacted with different styles.

Share this post


Link to post
7 minutes ago, Carlo Barazzetta said:

No, I didn't open a bug report because in my case it was just a demo of a component with on-the-fly style modification on the form itself to show how the component reacted with different styles.

Well, if nobody reports issues, chances they will be fixed is zero to none. Yes, there is always chance that EMBT developers themselves will eventually bump into the issue and make internal report, but those chances are rather slim.

 

I am not judging anyone here, reporting bugs takes time, but the more people report reproducible bugs they encounter, the less bugs there will be out in the wild. Yes, I also know that report alone does not mean that bug will be promptly fixed.

  • Like 1

Share this post


Link to post

10.4.1 and 10.3 for sure, I don't know about the rest.
I'll check if moving .Clear in BeforeDestruction of the frame helps. That could be a workaround for most of us.

Share this post


Link to post

I was not able to reproduce the issue. Tried with Form>PageControl>TabSheet>Frame>PageControl>TabSheet>TreeView.

Can you upload simplified demo project?

  • Like 1

Share this post


Link to post
2 minutes ago, pyscripter said:

The bug is not related to frames.

I had a guess about it, but since at me it is on a frame... but you are right, let me rephrase it to "moving Items.Clear to the owning component's BeforeDestruction event" to be more inclusive 🙂

Share this post


Link to post
2 minutes ago, balabuev said:

I was not able to reproduce the issue. Tried with Form>PageControl>TabSheet>Frame>PageControl>TabSheet>TreeView.

Can you upload simplified demo project?

My issue is that I have 5-6 TreeViews in my application in different locations and only one seems to be affected. I don't know what is the difference, this is what I was attempting to debug in the first place. If I know more, I will be able to make a small test case.

Share this post


Link to post
5 minutes ago, balabuev said:

I was not able to reproduce the issue.

In the SvgIconImageList demo case, the application was compiled with the Windows default style and the error occurs when you change the style at runtime.   I think that may be significant because it causes the controls to be recreated.

Share this post


Link to post
1 minute ago, pyscripter said:

In the SvgIconImageList demo case, the application was compiled with the Windows default style and the error occurs when you change the style at runtime.   I think that may be significant because it causes the controls to be recreated.

This is interesting. I am also changing the style runtime, when the application starts. However at this stage the frame including the TreeView does not exist yet in my case so I'd say it's irrelevant.

Share this post


Link to post
1 minute ago, pyscripter said:

the application was compiled with the Windows default style and the error occurs when you change the style at runtime.   I think that may be significant because it causes the controls to be recreated.

Tried this already, no luck. Btw, TTreeView with handle re-creation was always strange...

Share this post


Link to post

an MCVE would be nice as we could figure out which version are affected and also debug it instead of guessing

  • Like 1

Share this post


Link to post
48 minutes ago, pyscripter said:

 

I removed as much as possible from your demo. It's now independent from SVG image list, and refers only to standard components. I removed almost all code also.

Source.zip

 

What is interesting: removing the bottom list view will stop the bug from occurring.

 

Edited by balabuev
  • Like 5
  • Thanks 1

Share this post


Link to post
4 hours ago, balabuev said:

 

I removed as much as possible from your demo. It's now independent from SVG image list, and refers only to standard components. I removed almost all code also.

Source.zip

 

What is interesting: removing the bottom list view will stop the bug from occurring.

 

Very strange that the presence of an empty ImageView in the same form affects the memory leak on another component (the TTreeNodes of the TTreeView) after changing styles!

Share this post


Link to post
4 hours ago, balabuev said:

I removed as much as possible from your demo. It's now independent from SVG image list, and refers only to standard components. I removed almost all code also.

Source.zip

 

You forgot to remove the build event. I've tested with Delphi 10.4.2 and the bug is still there:
 

---------------------------
Unexpected Memory Leak
---------------------------
An unexpected memory leak has occurred. The unexpected small block leaks are:

13 - 20 bytes: UnicodeString x 2
21 - 28 bytes: UnicodeString x 4
45 - 52 bytes: TTreeNode x 6

---------------------------
OK   
---------------------------
 

PS. It looks like that seBorder in the TListView's Styleelement is the problem. Remove it and the leak dissapear!

Edited by Lajos Juhász

Share this post


Link to post

You can achieve the same with two TListView's on the same parent, like a TPanel, both having one node.

There is something with FTempItem or I don't know, TSubItems' constructor and destructor is not called the same time (4/3),

I have no more time for debugging and I don't want to see this code anymore.

I can't believe that on a theme change you have to destroy and create a component 4 times in a row.

 

Share this post


Link to post
9 hours ago, Attila Kovacs said:

destructor TWinControl.Destroy;

Hmm, now I cannot believe also. Will try to debug.

 

Edit: These four controls - are styled scrollbars (TScrollingStyleHook.TScrollWindow).

 

Edit: Workaround:

type
  TTreeViewFix = class(TComponent)
  private
    FView:   TTreeView;
    FOldWnd: TWndMethod;
  protected
    procedure Notification(C: TComponent; O: TOperation); override;
    procedure WndHook(var M: TMessage);
  public
    class procedure Apply(AView: TTreeView);
  end;

procedure TTreeViewFix.Notification(C: TComponent; O: TOperation);
begin
  inherited;
  if (C = FView) and (O = opRemove) then
  begin
    FView.WindowProc := FOldWnd;
    Free;
  end;
end;

procedure TTreeViewFix.WndHook(var M: TMessage);
begin
  if (M.Msg = WM_NCDESTROY) and (csDestroying in FView.ComponentState) then
    FView.Items.Clear;
  FOldWnd(M);
end;

class procedure TTreeViewFix.Apply(AView: TTreeView);
var
  fx: TTreeViewFix;
begin
  fx := TTreeViewFix.Create(nil);
  try
    fx.FView         := AView;
    fx.FOldWnd       := AView.WindowProc;
    AView.WindowProc := fx.WndHook;
    AView.FreeNotification(fx);
  except
    fx.Free;
    raise;
  end;
end;

procedure TMainForm.FormCreate(Sender: TObject);
begin
  TTreeViewFix.Apply(TreeView);
  //...
end;

 

Edited by balabuev
  • Like 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×