Delphi - Как предотвратить форматирование /MsgBoxes для перемещения по предыдущей форме?

Много раз после эпохи Windows 98 мы столкнулись с тем, что некоторые диалоги теряют свой Z-порядок и возвращаются к предыдущей форме.

Например:

Dialog1.ShowModal;

Dialog1.OnClickButton() : ShowMessage('anything');

Когда появляется MessageBox, он иногда не имеет фокуса и перемещается под Dialog1. Пользователи смущены этим, они говорят: мое приложение замерло!!! Но если они используют Alt + Tab для перехода в другое приложение и обратно, фокус возвращается в MessageBox, и это будет окно переднего плана.

Мы испытали это с ShowMessage, MessageBox, нормальными формами, а также с формами QuickReport.

Кто-нибудь знает об этом? Это ошибка Windows? Как вы можете это предотвратить? Как это поймать?

Спасибо за вашу помощь:  дд


Я действительно сказал, что ПОСЛЕ Win98, поэтому все ОС (Win7 также) затронуты этой проблемой. Мы использовали Delphi 6 Prof, поэтому свойства не работают с формами по умолчанию.

Кто-то сказал, что диалоговые окна сообщений управляются с помощью MessageBox + MB_APPLMODAL. Это хорошая новость, но у нас много старых форм и компонентов, сторонних инструментов.

Так что тяжелая работа - сделать совершенно новое приложение с заменой форм.

Но мы постараемся сделать это.

Я думаю, что ответ - это проблема с половинным приложением и проблема с половиной Windows. Если Windows иногда обрабатывает это, а иногда и нет - кажется, это ошибка Windows. Но если мы сможем заставить хорошие модальные окна сделать, это ошибка программирования.

Может кто-нибудь объяснить мне, что означает флаг WS_POPUP? Имеет ли он какой-то побочный эффект или нет?

Спасибо: дд

Ответы

Ответ 1

То, что для PopupMode и PopupParent свойств для.

Например, вы можете:

Dialog1.PopupMode := pmExplicit;
Dialog1.PopupParent := self;
Dialog1.ShowModal;

Это говорит Windows о правильном Z-порядке.

Ответ 2

Для старых версий delphi (до Delphi 2007), в формах OTHER, чем ваша основная форма:

interface
  TMyForm = Class(TForm)
  protected
    procedure CreateParams(var Para: TCreateParams); override;
  end;
...
implementation
...
procedure TMyForm.CreateParams(var Para: TCreateParams);
begin
  inherited;
  Para.Style := Para.Style or WS_POPUP;
  { WinXP Window manager requires this for proper Z-Ordering }
  // Para.WndParent:=GetActiveWindow;
  Para.WndParent := Application.MainForm.Handle;
end;

Для сообщений в MBF необходимо указать MB_TOPMOST:

Application.MessageBox(PChar(amessage), PChar(atitle),    otherflags or MB_TOPMOST);

Ответ 3

Я просмотрел эту страницу и часто задаваемые вопросы в течение получаса и все еще не могу найти, как опубликовать комментарий, поэтому простите меня за это нарушение протокола.

Прежде всего, я хотел бы пояснить, что плакат IMHO не использует Windows 98. Он пишет "после эпохи Windows 98", который, как я понимаю, означает, что у него такая проблема с версиями Windows после 98.

Поскольку у меня тоже есть эта проблема (CB2009), я хотел бы подчеркнуть вопрос о плакате "Это ошибка Windows?", о которой я не видел. Если это ошибка Delphi/Builder, возможно, есть способ избежать этого? Я не вижу, как перехватывать все потенциальные диалоги является работоспособным решением или не использовать fsStayOnTop. У меня есть форма настроек, которая должна оставаться поверх моей основной формы, но форма настроек может включать и вызывать всплывающие диалоги, которые при определенных условиях исчезают в форме настроек.

Было бы очень полезно, если бы я понял, где поддержка z-порядка идет не так, поскольку она может предложить ключ к тому, как этого избежать.

Ответ 4

Трюк, который я использовал недавно, заключался в применении этих двух строк кода при создании каждой формы:

SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or 
  WS_EX_APPWINDOW or WS_EX_TOPMOST);
SetWindowLong(Handle, GWL_HWNDPARENT, GetDesktopWindow);

Ручка - это дескриптор формы (Form1.Handle). Часть WS_EX_APPWINDOW делает каждое окно появляться на панели задач, удаляя его, если вы не хотите этого дополнительного эффекта.

В моей основной форме я использую эту строку:

SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or
  WS_EX_TOPMOST);

Я также использую эту функцию для создания собственных диалоговых окон (я создал новую функцию для каждого стиля диалога - ошибка, подтверждение и т.д.):

function CustomDlg(const AMessage : string; const ADlgType: TMsgDlgType;
  const AButtons: TMsgDlgButtons; const ADefaultButton: TMsgDlgBtn) : TForm;
begin
  Result := CreateMessageDialog(AMessage, ADlgType, AButtons, ADefaultButton);
  with Result do
    begin
      SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or 
        WS_EX_APPWINDOW or WS_EX_TOPMOST);
      SetWindowLong(Handle, GWL_HWNDPARENT, GetDesktopwindow);
      FormStyle := fsStayOnTop;
      BringToFront;
    end;
end;

Конечно, часть FormStyle := fsStayOnTop; является необязательной, но я использую ее, чтобы убедиться, что мои диалоги подтверждения и ошибки всегда отображаются пользователю.

Кажется, что немного работы, но чистый эффект заключается в том, что мне больше не нужно беспокоиться о формах, случайно скрывающихся за другими формами.