Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

In this Discussion

Crash after freeing TCustomControl on TLMDDockPanel (focus problem)

If a descendent of TCustomControl is dynamically created on a TLMDDockPanel then dynamically freed then it can cause a crash due to the control's canvas being accessed after it has been destroyed.

There seem to be some essential pre-conditions.
1. The control initially does not have focus.
2. In the event handler that causes the control to be destroyed, the control is briefly given focus just before it is destroyed. In my test case this is caused by a ShowMessage call (e.g. to ask user confirmation) but FocusControl(fControl) also has the same effect.

The crash occurs only if a TLMDDockPanel is a parent of the control. It doesn't occur with a regular TPanel or if the control is a direct child of TForm. This is why I think the fault is in TLMDDockPanel.

Unfortunately I can't seem to upload a zip file containing the test case, so here it is:

unit Unit1;
interface
uses
  Winapi.Windows, Winapi.Messages, System.Classes, Vcl.Controls, Vcl.Forms,
  Vcl.Menus, LMDDckSite;

type
  TMyControl = class (TCustomControl)
  protected
    procedure CMFocusChanged(var Message: TCMFocusChanged); message CM_FOCUSCHANGED;
  public
    constructor Create(AOwner: TComponent); override;
  end;

  TForm1 = class(TForm)
    MainMenu: TMainMenu;
    CrashMenuItem: TMenuItem;
    LMDDockSite1: TLMDDockSite;
    LMDDockPanel1: TLMDDockPanel;
    procedure CrashMenuItemClick(Sender: TObject);
    procedure FormShow(Sender: TObject);
  private
    fControl : TMyControl;
    procedure WMUser(var aMsg: TMessage); message WM_USER;
  end;

var
  Form1: TForm1;

implementation
uses
  Vcl.Dialogs;

{$R *.dfm}

constructor TMyControl.Create(AOwner: TComponent);
begin
  inherited;
  Width   := 75;
  Height  := 25;
  TabStop := True;
end;

procedure TMyControl.CMFocusChanged(var Message: TCMFocusChanged);
begin
  Invalidate;
  inherited;
end;

procedure TForm1.CrashMenuItemClick(Sender: TObject);
begin
  ShowMessage('Message');
//  FocusControl(fControl);
  fControl.Free;
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  PostMessage(Handle, WM_USER, 0, 0);
end;

procedure TForm1.WMUser(var aMsg: TMessage);
begin
  fControl := TMyControl.Create(LMDDockPanel1);
  fControl.Parent := LMDDockPanel1;
  fControl.Top    := 23;
end;
end.


You'll have to drop a TLMDDockSite on the form, add a TLMDDockPanel, and also a TMainMenu with a single menu item that invokes CrashMenuItemClick. Run the application, then without clicking on anything else, invoke the Crash menu item. Dismiss the dialog, and you should get an access violation.

I'm using TLMDTools 2013, update 3. Is this problem fixed in the latest version? If not, will you fix it and if so when?

Many thanks.

Comments

  • 7 Comments sorted by Votes Date Added
  • Hi,

    I can confirm this issue, but was unable yet to find the exact problem...
  • Thank you for confirming that. Did you test it in the latest 2014 release or just 2013.3?
  • I've tested in our private very latest development version.
  • I think the problem is that TLMDDockPanel.ActiveChanged should not call Update, because this sends a WM_PAINT to all child controls that need repainting. The TCustomControl descendent is marked as needing repainting, because its Invalidate method has just been called, but its canvas has also just been destroyed so it crashes when it receives WM_PAINT.

    My workaround is to set the child control's Parent property to nil just before freeing it. This prevents it from receiving WM_PAINT from its parent, because it no longer has a parent.
  • Hi,

    You may be right. The question is how to fix this. Because if the panel need to be repainted, what can I do else?
  • Would it be sufficient to call Invalidate? That way the painting will not happen until after the child control has been destroyed. (I haven't tried this.)
Sign In or Register to comment.