Почему TApplication.MessageBox закрывается автоматически? - PullRequest
2 голосов
/ 15 февраля 2020

У меня есть приложение, которое загружается за несколько секунд (много инициализаций). GUI зависает при запуске. Поэтому я хочу создать экран spla sh, который постепенно исчезает при загрузке приложения. Я использую компонент 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;

И это экран spla sh:

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 не (автоматически закрывается).

Почему закрытие TfrmSpla sh также закроет окно сообщения?

1 Ответ

5 голосов
/ 15 февраля 2020

TApplication.MessageBox - это оболочка для функции 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

Обратите внимание, что HWND, переданный на вызов WinAPI, равен TaskActiveWindow, который принимается как активное окно во время вызова (если только его нет, и в этом случае вместо него используется дескриптор приложения). Поскольку вы только что создали свой TFrmSpla sh, он будет активным окном, и окно сообщения будет удалено, когда его родитель (окно spla sh) будет закрыт.

Когда вы просто вызываете MessageBox напрямую:

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

Вы передаете Handle, который неявно является дескриптором формы, из которой вы вызываете код, в данном случае ваш TMainForm, поэтому основная форма становится владельцем в этом случае и не имеет отношения к экрану spla sh.

...