为什么在通过OnClose事件(在线程中)释放表单后不显示MessageDlg

时间:2015-02-02 03:02:34

标签: multithreading delphi dialog delphi-xe6

鉴于这种情况:

procedure TForm2.Button1Click(Sender: TObject);
var
  Form: TForm;
begin
  Form := TForm.Create(nil);
  Form.OnClose := FormClosed;
  Form.Show;
  Sleep(200);
  TThread.CreateAnonymousThread(
    procedure
    begin
      TThread.Synchronize( nil, 
        procedure 
        begin 
          Form.Close; 
          MessageDlg('Testing', mtInformation, [mbok], 0); 
        end);
    end).Start;
end;

procedure TForm2.FormClosed(Sender: TObject; var Action: TCloseAction);
begin
  Action := TCloseAction.caFree;
end;

我的MessageDlg调用未显示(此调用的结果始终为mrCancel(2))。

在挖掘之后,它与OnClose事件相关并将Action设置为caFree。

Form.Close更改为Form.Free并删除OnClose事件会完全显示MessageDlg。在调用Form.Close之前放置MessageDlg 可以正常工作。最初我认为我的Form变量的范围可能导致了问题,但在TForm2实例中声明Form作为私有字段并没有解决问题。

我的目标是显示一个启动窗体,执行我的线程,然后通过所述线程的回调,关闭Splash窗体并在适当的位置显示对话框。

为清楚起见,为什么会发生这种情况?

2 个答案:

答案 0 :(得分:7)

正在发生的事情是对话框的拥有窗口是正在关闭的表单。当对话框启动其模态消息循环时,表单将被释放并随之取消其拥有的窗口。包括对话框。

对此进行测试,通过替换调用以显示对话框

,让您更加放心我上面所述的内容是正确的。
MessageBox(0, ...);

然后用

MessageBox(Form.Handle, ...);

也就是说,明确对话框的所有者。

第一个版本没有所有者,将显示该对话框。第二个赢了,因为它复制了代码中的场景。

答案 1 :(得分:3)

Windows运行时要求可视窗口的消息由在创建该窗口的同一线程中运行的消息循环处理。

Windows API还强制规定可以在窗口之外执行哪些操作,而不是创建窗口的线程。即确实很少,除了向其发送或发送消息。

考虑到这些信息,我们可以解释您案件中发生的情况。

Form.Close 方法最终通过向其发布消息(CM_RELEASE)来关闭表单。但是在您的实现中,负责响应该消息的消息循环 - 应用程序主消息循环 - 由于消息是从 Synchronize()方法中发布而被阻止。

即。您的Synchronize()方法发布消息以关闭表单,但在您的Synchronize()方法完成之前,该表单窗口不能处理该消息,并且在用户响应消息之前不会发生该消息你用这种方法呈现的方框。

我希望这可以帮助您了解代码中发生的事情。