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
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.