Delphi-как узнать, какой модальный диалог имеет фокус и вывести его на передний план?

у меня есть приложение Delphi 2006, которое может всплывать модальный диалог в ответ на условие ошибки. Кажется, он попадает в состояние, когда один из этих модальных диалогов открыт, расположен перед основной формой, но ни одна из форм не отвечает на сообщения. Нажатие на любой из них дает "bonk". Приложение работает нормально, пользовательский интерфейс обновляет основную форму, но вы ничего не можете сделать. Я думаю, что скорее всего еще один модальный диалог под основной формой. Будь это моя или одна из Windows я понятия не имею.

другие статьи:

  • приложение реагирует на сочетания клавиш OK. Один из этих shortuts выключает приложение изящно, и это сработало. С тех пор я не могу воспроизвести ситуацию.
  • приложение иконку в трее. Это реагирует на щелчки Правой Кнопкой Мыши. Если я сверну приложение отсюда, основная форма минимизирует и оставляет модальный диалог отображаемым, все еще без фокуса. Если я восстановлю основную форму, все будет так, как есть были, ни окно с фокусом. Alt-tab имеет аналогичные результаты.
  • платформа Windows 7
  • Я вызываю DisableProcessWindowsGhosting перед созданием любых форм
  • Я открываю модальные диалоги с

    ModalDialog.PopupParent := MainForm ;
    ModalDialog.ShowModal ;
    
  • Я откладываю эти диалоги ошибок, если открыты другие модальные диалоги:

    if (Application.ModalLevel = 0) then
        {open modal dialog}
    

мой вопрос состоит из двух частей:

есть ли способ программно узнать какое окно имеет фокус? Затем я мог бы предпринять некоторые действия для этого сценария или в крайнем случае я мог бы предоставить им ярлык, чтобы вывести его на передний план или предпринять некоторые уклончивые действия (в зависимости от диалога), например, установить ModalResult в mrCancel.

как может возникнуть такая ситуация? Обычно, когда я получаю модальный диалог за основной формой (я могу сделать это, открыв модальный диалог, минимизируя приложение из значка в трее, а затем снова восстанавливая приложение - основная форма приложения восстанавливается в перед диалогом, при этом диалог все еще сохраняет фокус), я могу снова вывести его на передний план, нажав на значок в трее или закрыть его с помощью Esc ключ, но в этом случае он не работал.

**обновление**

исправление Миши работало отдельно от диалогов не-delphi, таких как TSaveDialog. Я смог заставить их работать, добавив Application.ModalPopupMode := pmAuto ; непосредственно перед вызовом Execute.

под "теперь" я имею в виду что диалог сохранения был впереди после следующей последовательности:

  • открыть диалоговое окно Сохранить
  • свернуть приложение из трея
  • восстановить приложение из значка в трее

тогда как он был за основной формой без ModalPopupMode := pmAuto.

так что я надеюсь, что эти изменения помогут проблему (пока unreproduced).

4 ответов


последнее активное всплывающее окно (VCL или нет) можно запросить с помощью GetLastActivePopup:

function GetTopWindow: HWND;
begin
  Result := GetLastActivePopup(Application.Handle);
  if (Result = 0) or (Result = Application.Handle) or
      not IsWindowVisible(Result) then
    Result := Screen.ActiveCustomForm.Handle;
end;

это несколько скопировано из TApplication.BringToFront.

приведение этого окна к фронту может быть сделано SetForegroundWindow:

SetForegroundWindow(GetTopWindow);

отметим, что Application.BringToFront может сделать трюк в целом, но я однажды испытал, что он не работает должным образом, ситуация, которую я не смог воспроизвести с тех пор.


Если форма с фокусом слишком долго отвечает на сообщения (Form1), так что Windows думает, что Form1 не отвечает, А Form1 затем отображает модальную форму (Form2), после того, как Form2 отображается и приложение снова обрабатывает сообщения, Form1 будет выведен на передний план, тем самым потенциально "покрывая" Form2.

положить это в приложение.Onidle event сделает трюк:

  if Assigned(Screen.ActiveForm) then
  begin
    if (fsModal in Screen.ActiveForm.FormState) and
       (Application.DialogHandle <= 0)) then 
    begin
      Screen.ActiveForm.BringToFront;
    end;
  end;

GetForegroundWindow () - это функция, которую вы ищете, если вы знаете название или ручку модальном окне это простой.

HWND GetForegroundWindow ();

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

http://msdn.microsoft.com/en-us/library/windows/desktop/ms633505%28v=vs.85%29.aspx


я использовал решение Миши и работал немного дальше (используя код NGLN), чтобы решить проблемы, которые видел rossmcm (handlings non VCL dialogs).

следующий код работает I таймер:

type
  TCustomFormAccess = class(TCustomForm);


if Assigned(Screen.ActiveCustomForm) then
begin
  if ((fsModal in Screen.ActiveCustomForm.FormState) and
      (Application.DialogHandle <= 0)) then
  begin
    TopWindow := GetLastActivePopup(Application.Handle);
    TopWindowForm := nil;
    for i := 0 to Screen.CustomFormCount - 1 do
    begin
      CustomFormAccess := TCustomFormAccess(Screen.CustomForms[i]);
      if CustomFormAccess.WindowHandle = TopWindow then TopWindowForm := CustomFormAccess;
    end;
    if Assigned(TopWindowForm) and (Screen.ActiveCustomForm.Handle <> TopWindow) then
    begin
      Screen.ActiveCustomForm.BringToFront;
    end;
  end;
end;