我写的自定义控件在销毁时会导致崩溃.很难确定具体情况,这可能是控制由第三方控制作为父级的一个因素.
编辑2014年10月8日
我现在有了一个更好的SSCCE来说明在TForm上只使用TMediaPlayer(来自Delphi VCL)的崩溃.所以我删除了之前写的很多内容.请查看编辑历史记录. (事实证明,前一个调用堆栈中的CM_EXIT是一个红色鲱鱼.)
这是SSCCE:
unit Unit1;
interface
uses
System.Classes, Vcl.Controls, Vcl.Forms, Vcl.Menus, Vcl.MPlayer;
type
TForm1 = class(TForm)
MainMenu: TMainMenu;
CrashMenuItem: TMenuItem;
procedure CrashMenuItemClick(Sender: TObject);
procedure FormShow(Sender: TObject);
private
fControl : TMediaPlayer;
end;
var
Form1: TForm1;
implementation
uses
Vcl.Dialogs;
{$R *.dfm}
procedure TForm1.CrashMenuItemClick(Sender: TObject);
begin
ShowMessage('Message');
fControl.Free;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
fControl := TMediaPlayer.Create(Form1);
fControl.Parent := Form1;
end;
end.
在释放控件之前立即调用ShowMessage至关重要.
>解除对话框后,TMediaPlayer控件获取WM_SETFOCUS.
>然后调用它的析构函数.继承TCustomControl.Destroy释放画布,然后继承TWinControl.Destroy调用TWinControl.RemoveFocus,因此它获得WM_KILLFOCUS.
> TMediaPlayer.WMKillFocus直接调用Paint,它尝试使用释放的画布和崩溃.
(之前我有一个自定义控件,其中CMFocusChanged称为Invalidate.效果相同,但调用堆栈更多涉及.)
我原来的3个问题,NGLN已在下面回答:
>我只是在调用FreeAndNil(fMyControl)时做错了什么?在破坏它之前我必须先将它取消它吗?但是这对于任何其他控件来说似乎都没有必要,所以更有可能只隐藏底层错误.
>我的控件是否应该在其析构函数中修复此问题,以便TWinControl知道不要尝试重新绘制它?
>第三方家长控制中是否有错误?是否一旦它开始被销毁,我的控件肯定不会收到WM_PRINTCLIENT消息? (当我控制失去焦点时,第三方控件似乎在接收CM_EXIT时显式调用其继承的TWinControl.Update.)
但真正的问题仍然存在:我的SSCCE中的代码有什么问题,或者Delphi VCL中是否有错误?
(顺便提一下,TCustomControl的任何后代都会出现同样的问题.为方便起见,我使用了TMediaPlayer.)
最佳答案
Am I doing something wrong merely calling
FreeAndNil(fMyControl)
?
不,只要对控件的所有引用都被清除(nilled)并且实例的代码不再运行,就应该能够在任何给定时间释放每个控件.
Must I unparent it before destroying it? But this doesn’t seem necessary with any other controls, so more likely that will just hide the underlying bug.
不,确实没必要.
Should my control have something in its destructor to fix this so that
TWinControl
knows not to try to repaint it?
不,通常没有必要. VCL已经全部内置.出于测试目的或作为(临时)变通方法,您可以尝试使用if if(在ComponentState中的csDestroying)中覆盖PaintWindow.
Is there perhaps a bug in the 3rd party parent control? Is it the case that my control should certainly not receive a
WM_PRINTCLIENT
message once it has started to be destroyed? (The 3rd party control seems to make an explicit call to its inherited TWinControl.Update when it receivesCM_EXIT
as a consequence of my control losing focus.)
父控件确实接收CM_EXIT,因为它具有焦点控制,现在它已经不再存在(即Form.ActiveControl = nil).这是正常的行为.至于为什么父母将WM_PRINTCLIENT发送给控件(你怎么知道这个请求来自父级?它似乎从Update调用开始.)我不知道.要排除有缺陷的父级的可能性,请使用其他父级重试您的案例.
更新(由于问题编辑):
TMediaPlayer.WMKillFocus
callsPaint
directly…
procedure TMediaPlayer.WMKillFocus(var Message: TWMKillFocus);
begin
Paint;
end;
那是禁忌!这绝对是VCL中的一个错误.除了通过WM_PAINT消息绘制请求之外,永远不应该直接调用绘制.我有submitted a report on QC.
(Previously I had a custom control where
CMFocusChanged
calledInvalidate
. The effect was the same but the call stack was rather more involved.)…
(Incidentally, the same problem will occur with any descendent of
TCustomControl
. I usedTMediaPlayer
for convenience.)
在D7和XE2中进行测试的情况并非如此.