释放集中的自定义控件会导致崩溃

时间:2014-10-06 15:57:20

标签: delphi delphi-xe2

我写的自定义控件在销毁时会导致崩溃。 很难确定具体情况,这可能是控制权是第三方控制权的一个因素。

编辑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至关重要。

  1. 关闭对话框后,TMediaPlayer控件获得WM_SETFOCUS
  2. 然后调用它的析构函数。继承TCustomControl.Destroy释放画布,然后继承TWinControl.Destroy次调用TWinControl.RemoveFocus,因此获得WM_KILLFOCUS
  3. TMediaPlayer.WMKillFocus直接调用Paint,尝试使用已释放的画布并崩溃。
  4. (之前我有一个自定义控件CMFocusChanged称为Invalidate。效果相同,但调用堆栈更多涉及。)

    我最初的3个问题,NGLN已在下面回答:

    1. 我只是在调用FreeAndNil(fMyControl)时做错了什么?在破坏它之前我必须先将它取消它吗?但是,对于任何其他控件来说,这似乎并不合适,因此更有可能隐藏底层错误。
    2. 我的控件是否应该在其析构函数中有一些东西来修复它,以便TWinControl知道不要重新绘制它?
    3. 第三方家长控制中是否有错误?是否一旦它开始被销毁,我的控件肯定不会收到WM_PRINTCLIENT消息? (当我控制失去焦点时,第三方控件似乎在接收CM_EXIT时显式调用其继承的TWinControl.Update。)
    4. 但真正的问题仍然存在:我的SSCCE中的代码有什么问题,或者Delphi VCL中是否有错误?

      (顺便提一下,TCustomControl的任何后代都会出现同样的问题。为方便起见,我使用了TMediaPlayer。)

1 个答案:

答案 0 :(得分:2)

  

我只是在调用FreeAndNil(fMyControl)时做错了吗?

不,每个控件都应该能够在任何给定时间释放,只要清除(nilled)对控件的所有引用并且实例的代码不再运行。

  

在销毁它之前,我是否必须将其取消隐藏?但是,对于任何其他控件来说,这似乎并不合适,因此更有可能隐藏底层错误。

不,确实没必要。

  

我的控件是否应该在其析构函数中有一些东西来修复它,以便TWinControl知道不要尝试重新绘制它?

不,通常没有必要。 VCL已经全部内置。出于测试目的或作为(临时)变通方法,您可以尝试使用PaintWindow覆盖if not (csDestroying in ComponentState) then

  

第三方家长控件中是否有错误?一旦它开始被销毁,我的控件是否应该肯定不会收到WM_PRINTCLIENT消息? (当我控制失去焦点时,第三方控件似乎在接收CM_EXIT时对其继承的TWinControl.Update进行显式调用。)

父控件确实收到CM_EXIT,因为它有一个集中控制,现在它已经没有了(即Form.ActiveControl = nil)。这是正常的行为。至于为什么父母向控件发送WM_PRINTCLIENT(你怎么知道这个请求来自父母?它似乎从Update电话开始。)我不知道。要排除有错误的父母的可能性,请使用其他父母重试您的案例。


更新(由于问题编辑):

  
      
  1. TMediaPlayer.WMKillFocus直接致电Paint ......
  2.   
procedure TMediaPlayer.WMKillFocus(var Message: TWMKillFocus);
begin
  Paint;
end;

这是禁忌!这绝对是VCL中的一个错误。除了通过Paint消息进行绘画的请求之外,永远不应该直接调用WM_PAINT。我有submitted a report on QC

  

(之前我有一个自定义控件CMFocusChanged称为Invalidate。效果相同,但调用堆栈更多涉及。)

     

...

     

(顺便提一下,TCustomControl的任何后代都会出现同样的问题。为方便起见,我使用了TMediaPlayer。)

D7和XE2中的测试情况并非如此。