为什么 TApplication.MessageBox 自动关闭?
Why TApplication.MessageBox closes automatically?
我有一个应用程序需要几秒钟才能加载(大量初始化)。 GUI 在启动期间冻结。所以我想创建一个在应用程序加载时淡入淡出的启动画面。
我使用 TBackgroundWorker 组件在后台线程中制作动画。
但是,当我使用这个组件时,发生了一些奇怪的事情:当它发出信号 "work complete"(参见 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 也会关闭消息框?
TApplication.MessageBox is a wrapper around the WinAPI 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
,在调用时被当作活动的window(除非有none,在在这种情况下,将使用应用程序的句柄)。由于您刚刚创建了 TFrmSplash,它将处于活动状态 window 并且消息框将在其父级(您的启动画面 window)关闭时被释放。
当您直接调用 MessageBox 时:
MessageBox(Handle, 'Hi', nil, MB_OK); // This remains on screen
您正在传递 Handle
,它隐式地是您从中调用代码的表单的句柄,在本例中是您的 TMainForm
,因此主表单成为此中的所有者大小写,与启动画面无关。
我有一个应用程序需要几秒钟才能加载(大量初始化)。 GUI 在启动期间冻结。所以我想创建一个在应用程序加载时淡入淡出的启动画面。 我使用 TBackgroundWorker 组件在后台线程中制作动画。
但是,当我使用这个组件时,发生了一些奇怪的事情:当它发出信号 "work complete"(参见 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 也会关闭消息框?
TApplication.MessageBox is a wrapper around the WinAPI 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
,在调用时被当作活动的window(除非有none,在在这种情况下,将使用应用程序的句柄)。由于您刚刚创建了 TFrmSplash,它将处于活动状态 window 并且消息框将在其父级(您的启动画面 window)关闭时被释放。
当您直接调用 MessageBox 时:
MessageBox(Handle, 'Hi', nil, MB_OK); // This remains on screen
您正在传递 Handle
,它隐式地是您从中调用代码的表单的句柄,在本例中是您的 TMainForm
,因此主表单成为此中的所有者大小写,与启动画面无关。