Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi 动态释放聚焦的自定义控件会导致崩溃_Delphi_Delphi Xe2 - Fatal编程技术网

Delphi 动态释放聚焦的自定义控件会导致崩溃

Delphi 动态释放聚焦的自定义控件会导致崩溃,delphi,delphi-xe2,Delphi,Delphi Xe2,我编写的一个自定义控件在被销毁时与崩溃有关。很难确定确切的情况,这可能是该控件由第三方控件作为父控件的一个因素 编辑2014年10月8日 我现在得到了一个更好的SSCCE,它仅使用TForm上的TMediaPlayer(来自Delphi VCL)演示了崩溃。所以我删除了很多我以前写的东西。请查看编辑历史记录。(事实证明,前一个调用堆栈中的CM_出口是一个骗局。) 以下是SSCCE: unit Unit1; interface uses System.Classes, Vcl.Controls

我编写的一个自定义控件在被销毁时与崩溃有关。很难确定确切的情况,这可能是该控件由第三方控件作为父控件的一个因素

编辑2014年10月8日 我现在得到了一个更好的SSCCE,它仅使用TForm上的TMediaPlayer(来自Delphi VCL)演示了崩溃。所以我删除了很多我以前写的东西。请查看编辑历史记录。(事实证明,前一个调用堆栈中的CM_出口是一个骗局。)

以下是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\u SETFOCUS
  • 然后调用它的析构函数。继承的
    TCustomControl.Destroy
    释放画布,然后继承的
    TWinControl.Destroy
    调用
    TWinControl.RemoveFocus
    ,因此它获得一个
    WM\u KILLFOCUS
  • TMediaPlayer.WMKillFocus
    直接调用
    Paint
    ,试图使用释放的画布并崩溃
  • (以前我有一个自定义控件,其中
    CMFocusChanged
    名为
    Invalidate
    。效果相同,但调用堆栈更复杂。)

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

  • 仅仅调用
    FreeAndNil(fMyControl)
    ,我是否做错了什么?我必须先把它拆开再销毁吗?但这对于任何其他控件来说似乎都是不必要的,因此更可能只是隐藏了潜在的bug
  • 我的控件的析构函数中是否应该有一些东西来修复此问题,以便
    TWinControl
    知道不尝试重新绘制它
  • 第三方父控件中是否存在错误?在这种情况下,我的控件在开始被销毁后肯定不会收到WM_PRINTCLIENT消息吗?(由于我的控件失去焦点,当第三方控件接收到CM_退出时,它似乎显式调用其继承的
    TWinControl.Update
    。)
  • 但真正的问题仍然存在:我的SSCCE中的代码是否有任何错误,或者Delphi VCL中是否存在错误

    (顺便说一句,同样的问题也会发生在
    TCustomControl
    的任何后代身上。为了方便起见,我使用了
    TMediaPlayer
    。)

    仅仅调用
    FreeAndNil(fMyControl)
    ,我是否做错了什么

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

    我必须先把它拆开再销毁吗?但这对于任何其他控件来说似乎都是不必要的,因此更可能只是隐藏了潜在的bug

    不,确实没有必要

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

    不,通常没有必要。VCL已经内置了这一切。出于测试目的或作为(临时)解决方法,您可以尝试使用类似于
    的内容覆盖
    PaintWindow
    ,如果不是(组件状态下的csDestroying),则覆盖

    第三方父控件中是否存在错误?是不是我的控件一旦开始被销毁就肯定不会收到
    WM_PRINTCLIENT
    消息?(第三方控件似乎对其继承的TWinControl进行了显式调用。由于我的控件失去焦点,在收到
    CM_EXIT
    时更新。)

    父控件确实接收到
    CM\u EXIT
    ,因为它有一个聚焦控件,而现在不再有了(即
    Form.ActiveControl=nil
    )。所以这是正常的行为。至于家长为什么向控件发送
    WM_PRINTCLIENT
    (您如何知道该请求来自家长?它似乎从
    Update
    调用开始)我不知道。要排除出现问题家长的可能性,请使用其他家长重试您的案例


    更新(由于编辑问题):
  • TMediaPlayer.WMKillFocus
    直接调用
    Paint
  • 这是禁忌!这绝对是VCL中的一个bug<除了通过
    WM_Paint
    消息请求绘制外,不得直接调用code>Paint
    。我有

    (以前我有一个自定义控件,其中
    CMFocusChanged
    名为
    Invalidate
    。效果相同,但调用堆栈更复杂。)

    (顺便说一句,同样的问题也会发生在
    TCustomControl
    的任何后代身上。为了方便起见,我使用了
    TMediaPlayer
    。)


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

    是否从控件内的事件处理程序(例如OnClick)调用FreeAndNil?@bummi FreeAndNil(间接)从TAction事件处理程序调用。如果这是您的意思,那么就不要单击正在销毁的控件。VCL控件在其自身失去焦点时接收
    CM\u EXIT
    ,而不是在子控件失去焦点时。所以问题是,为什么
    tlmdockpanel
    接收
    CM_退出
    DefocusControl()
    只有一个代码路径,即如果要散焦的控件是窗体的
    ActiveControl
    。它调用
    TCustomForm.SetWindowFocus()
    TCustomForm.ActiveChanged()
    ,我在堆栈跟踪中看不到这两个函数。不能释放控件。而是使用
    Release
    ,然后将引用设置为nil。控件将在安全运行时释放自身。@NGLN:DockPanel不是开始时的焦点控件,因此它不应该接收
    CM\u EXIT
    ,除非它先获得焦点,然后又失去焦点。
    Update()
    如果正在更新的控件的任何部分事先已无效,则会触发重新绘制。所以也许
    procedure TMediaPlayer.WMKillFocus(var Message: TWMKillFocus);
    begin
      Paint;
    end;