Modale Fenster in den Vordergrund holen

Möglicherweise kennen Sie das Phänomen: ein modales Fenster (z.B. ein Dialog) gerät völlig unmotiviert in den Hintergrund. Evtl. bekommen Sie es mit ALT + TAB wieder nach vorn, aber auch das klappt nicht immer. Um dies zu beheben, können Sie die Klasse aus der nachfolgenden Unit benutzen.
(**************** ModalFix - eine Hilfsklasse für modale Fenster **************)
(*                                                                            *)
(* Copyright(c) 2011 Detlef Heibing                                           *)
(*                                                                            *)
(******************************************************************************)

unit uModalFix;

interface

uses Windows;

type
  TModalFix = class
  private
    class function GetModalWindow(SomeWindow: HWnd): HWnd;
    class function BringToForeground(aWnd: HWnd): Boolean;
    class function IsThreadTopWindow(aWnd: HWnd): Boolean;
  public
    class function FixModalWindow(AppWnd: HWnd): Boolean;
  end;

implementation

uses Classes;

{ TModalFix }

class function TModalFix.BringToForeground(aWnd: HWnd): Boolean;
const
  DRAWFLAGS = RDW_ERASE or RDW_INVALIDATE or RDW_UPDATENOW or RDW_ALLCHILDREN;
  MOVEFLAGS = SWP_NOMOVE or SWP_NOSIZE;
  SHOWMOVEFLAGS = MOVEFLAGS or SWP_SHOWWINDOW;
begin
  Result := true;
  (* Das Handle muss gültig und das Fenster sichtbar sein, sonst macht es ja
     keinen Sinn. *)
  if (aWnd <> 0) and IsWindowVisible(aWnd) then
    begin
      (* - Erst vor ALLE anderen Fenster bringen
         - Dann das TOPMOST entfernen, damit es nicht vor Fenstern anderer
           Programme bleibt
         - Und wieder nach vorn holen *)
      Result := SetWindowPos(aWnd, HWND_TOPMOST, 0, 0, 0, 0, MOVEFLAGS) and
                SetWindowPos(aWnd, HWND_NOTOPMOST, 0, 0, 0, 0, MOVEFLAGS) and
                SetWindowPos(aWnd, HWND_TOP, 0, 0, 0, 0, SHOWMOVEFLAGS);
      //Dieses muss dann aber neu gezeichnet werden (wieso auch immer)
      if Result then
        RedrawWindow(aWnd, nil, 0, DRAWFLAGS);
    end;
end;

class function TModalFix.FixModalWindow(AppWnd: HWnd): Boolean;
var
  ModalWnd: HWnd;
begin
  ModalWnd := GetModalWindow(AppWnd);
  if (ModalWnd <> 0) and not IsThreadTopWindow(ModalWnd) then
    Result := BringToForeground(ModalWnd)
  else
    Result := true;
end;

class function TModalFix.GetModalWindow(SomeWindow: HWnd): HWnd;
var
  WindowList: TList;
  hThread: DWORD;

  function EnumFunc(aWnd: HWnd; List: lParam): Boolean; stdcall;
  begin
    (* Nur Fenster, die
        - kein MDIChild
        - enabled und
        - sichtbar
       sind, in die Liste aufnehmen *)
    if ((GetWindowLong(aWnd, GWL_EXSTYLE) and WS_EX_MDICHILD) = 0) and
       IsWindowEnabled(aWnd) and IsWindowVisible(aWnd) then
      TList(List).Add(Pointer(aWnd));
    Result := true;
  end;

begin
  Result := 0;
  if SomeWindow <> 0 then
    begin
      WindowList := TList.Create;
      try
        //Thread-ID ermitteln, andere Threads interessieren uns ja nicht
        hThread := GetWindowThreadProcessID(SomeWindow);
        //...und durchgehen
        EnumThreadWindows(hThread, @EnumFunc, lParam(WindowList));
        //genau 1 aktives Fenster -> modal
        if WindowList.Count = 1 then
          Result := HWnd(WindowList[0]);
      finally
        WindowList.Free;
      end;
    end;
end;

class function TModalFix.IsThreadTopWindow(aWnd: HWnd): Boolean;
var
  NextWnd: HWnd;
  aThreadID, NextThreadID: DWORD;
begin
  Result := true;
  if aWnd <> 0 then
    begin
      aThreadID := GetWindowThreadProcessID(aWnd);
      NextWnd := GetWindow(aWnd, GW_HWNDPREV);
      (* Ermitteln eines Fensters, das zum selben Thread gehört, in der Z-Order
         weiter oben steht und sichtbar ist.
         Wird kein solches gefunden, muss aWnd ja das oberste Fenster der
         Applikation sein. *)
      while Result and (NextWnd <> 0) do
        begin
          NextThreadID := GetWindowThreadProcessID(NextWnd);
          if (NextThreadID = aThreadID) and IsWindowVisible(NextWnd) then
            Result := false
          else
            NextWnd := GetWindow(NextWnd, GW_HWNDPREV);
        end;
    end;
end;

end.
Als günstiger Aufrufzeitpunkt hat sich TApplication.OnIdle erwiesen, welches immer dann ausgeführt wird, wenn die Anwendung gerade nichts zu tun hat.
type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private-Deklarationen }
    procedure DoOnIdle(Sender: TObject; var Done: Boolean);
  public
    { Public-Deklarationen }
  end;

implementation

uses uModalFix;

procedure TForm1.DoOnIdle(Sender: TObject; var Done: Boolean);
begin
  if not TModalFix.FixModalWindow(Handle) then
    MessageBox(0, 'Fehler beim Wiederherstellen modaler Fenster', nil, 0);
  Done := true;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Application.OnIdle := DoOnIdle;
end;