【问题标题】:Why TApplication.MessageBox closes automatically?为什么 TApplication.MessageBox 会自动关闭?
【发布时间】:2020-05-31 03:59:17
【问题描述】:

我有一个应用程序需要几秒钟才能加载(大量初始化)。 GUI 在启动期间冻结。所以我想创建一个在应用程序加载时淡入淡出的启动画面。 我使用TBackgroundWorker 组件在后台线程中做动画。

但是,当我使用此组件时,会发生一些奇怪的事情:当它发出“工作完成”信号时(请参阅 BackgroundWorkerWorkComplete),我同时打开的消息对话框会自动关闭。

procedure TMainForm.ButtonStartSplashClick(Sender: TObject);
VAR
  frmSplash: TfrmSplash;
begin
 frmSplash:= TfrmSplash.Create(NIL);
 frmSplash.StartAnimation;

 //MessageBox(Handle, 'Hi', nil, MB_OK);   // This remains on screen
 Application.MessageBox(PChar('Hi'), PChar('Box'), MB_ICONINFORMATION); // This is automatically closed when the background thread is done
end;

这是启动画面:

procedure TfrmSplash.StartAnimation;
begin
 Show;
 BackgroundWorker.Execute;
end;


procedure TfrmSplash.FormClose(Sender: TObject; var Action: TCloseAction);
begin
 Action:= caFree;
end;


procedure TfrmSplash.BackgroundWorkerWork(Worker: TBackgroundWorker);
VAR i: Integer;
begin
  for i:= 1 to 255 DO
   begin
    AlphaBlendValue:= i; // do not access GUI directly from thread
    Sleep(30);
   end;
end;


procedure TfrmSplash.BackgroundWorkerWorkComplete(Worker: TBackgroundWorker; Cancelled: Boolean);
begin
 Close; // At this point, the msg box will be closed also
end;

我觉得奇怪的是 MessageBox 保留在屏幕上,而 Application.MessageBox 没有(自动关闭)。

为什么关闭 TfrmSplash 也会关闭消息框?

【问题讨论】:

  • 您阅读文档了吗? “您必须小心不要在 {OnWork} 事件处理程序中操纵任何用户界面对象。”不设置 AlphaBlendValue 时还会出现吗?
  • 刚刚测试过。仍然发生。由于 GUI 访问,我希望得到一些随机的 AV。但无论如何,这不是原因,因为它发生在我注释掉 AlphaBlendValue:= i
  • 为您的 AlphaBlend 代码使用报告进度事件。
  • @NasreddineGalfout - 好主意。谢谢/+1。但这不会解决消失的 msgbox 奇怪的问题。
  • @NasreddineGalfout 对于那个用例,老实说,我只会使用TTimer。没有必要为了阻止一个繁忙的循环定期创建简单的事件而启动一个线程。

标签: multithreading delphi backgroundworker splash-screen tform


【解决方案1】:

TApplication.MessageBoxWinAPI MessageBox 函数的包装器。前者的代码向您展示了它是如何被调用的:

function TApplication.MessageBox(const Text, Caption: PChar; Flags: Longint): Integer;
var
  ActiveWindow, TaskActiveWindow: HWnd;
  MBMonitor, AppMonitor: HMonitor;
  MonInfo: TMonitorInfo;
  Rect: TRect;
  FocusState: TFocusState;
  WindowList: TTaskWindowList;
begin
  ActiveWindow := ActiveFormHandle;
  if ActiveWindow = 0 then
    TaskActiveWindow := Handle
  else
    TaskActiveWindow := ActiveWindow;

   {  ... }


  try
    Result := Winapi.Windows.MessageBox(TaskActiveWindow, Text, Caption, Flags);
  finally

请注意,传递给 WinAPI 调用的 HWND 是 TaskActiveWindow,它在调用时被视为活动窗口(除非没有,在这种情况下使用应用程序的句柄代替)。由于您刚刚创建了 TFrmSplash,它将成为活动窗口,并且消息框将在其父窗口(您的启动窗口)关闭时被释放。

当你直接调用 MessageBox 时:

 MessageBox(Handle, 'Hi', nil, MB_OK);   // This remains on screen

您正在传递Handle,它隐含地是您从中调用代码的表单的句柄,在这种情况下是您的TMainForm,因此在这种情况下主表单成为所有者并且与启动画面。

【讨论】:

  • Man.... 在文档中,Embarcadeor 说“MessageBox 的 TApplication 封装会自动提供 Windows API 函数所需的缺失窗口句柄参数”。我认为使用 TApplication.MessageBox 会更好,因为它们可以解决缺少句柄的问题。他们“解决”了这个问题,但让问题变得更加模糊。
  • 在这种情况下使用 MessageBox(Handle, etc) 是不是更干净?
  • @Happy - Application.Messagebox 是模态的。对于设计师来说,很难想象您会从线程中删除所有者窗口!..
  • @HappyBirthdayDelphi_25 您可以在开始动画之前显示 MessageBox。
  • @SertacAkyuz - 我发誓如果文档会提到这一点,我不会。无论如何,我的错误是我没有查看 MessageBox 代码。我倾向于认为问题出在 bck 线程中,而我对此进行了调查。我的错! ms-help://embarcadero.rs_xe7/libraries/Vcl.Forms.TApplication.MessageBox.html
猜你喜欢
  • 2013-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多