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

Why is MessageDlg not shown after a form is freed via OnClose event (in a thread)

鉴于这种情况:

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 ok。将 MessageDlg 放在 对 Form.Close 的调用之前可以正常工作。最初我认为我的 Form 变量的范围可能导致了问题,但是在 TForm2 实例中声明 Form 为私有字段并不能解决问题。

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

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

Windows 运行时要求视觉 window 的消息由创建 window 的同一线程中的消息循环 运行 处理。

Windows API 还强制执行有关可以从创建 window 的线程以外的线程对 window 执行哪些操作的规则.即确实很少,除了向它发送或发布消息。

考虑到这些信息,我们可以解释您的情况。

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

即您的 Synchronize()d 方法发布消息以关闭表单,但是该消息不能也不会被该表单处理 window 直到您的 Synchronize()d 方法完成,并且直到用户已经完成回复了您在该方法中显示的消息框。

我希望这能帮助您理解代码中发生的事情。

正在发生的事情是对话框的所有者 window 是正在关闭的表单。当对话框开始其模态消息循环时,表单将被释放并取下其拥有的 windows。包括对话框。

测试一下,让您更加确信我上面所说的是正确的,方法是将首先显示对话框的调用替换为

MessageBox(0, ...);

然后

MessageBox(Form.Handle, ...);

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

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