为什么在通过 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, ...);
也就是说,明确对话的所有者。
没有所有者的第一个版本将显示对话框。第二个不会,因为它在您的代码中复制了场景。
鉴于这种情况:
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, ...);
也就是说,明确对话的所有者。
没有所有者的第一个版本将显示对话框。第二个不会,因为它在您的代码中复制了场景。