Die Anwendung in die TNA minimieren


In diesem Kapitel soll es hauptsächlich darum gehen, wie man die eigene Anwendung in die "Taskbar Notification Area" minimieren lassen kann. In Foren wird ab und zu gefragt, wie man das bewerkstelligen kann. Bei nonVCL-Programmen ist dies relativ einfach. Beim Erzeugen des Fensters laden wir das gewünschte Symbol und weisen das Fenster-Handle zu (ganz so, wie es in den letzten Kapiteln demonstriert wurde):

  WM_CREATE:
    begin
      NID.wnd   := wnd;
      NID.hIcon := LoadIcon(hInstance,'JIM');
    end;

Und beim Beenden des Programms entfernen wir sicherheitshalber das TNA-Icon (auch so, wie es bereits besprochen wurde):

  WM_DESTROY:
    begin
      Shell_NotifyIcon(NIM_DELETE,@NID);
{ ... } end;

So weit nichts Neues.

Um auf das Minimieren des Fensters zu reagieren, gibt es zwei Wege, die wir gehen können. Einer wäre, die Nachricht "WM_SYSCOMMAND" zu bearbeiten, wenn der wParam den Wert "SC_MINIMIZE" enthält. Allerdings reagiert unser Programm dann nicht auf den globalen System-Hotkey WIN+M, mit dem man recht bequem alle Fenster minimieren kann (sofern Ihre Tastatur die Taste mit dem Windows-Logo enthält, natürlich!). Das heißt, unser Programm reagiert schon, aber es wird wirklich nur minimiert, nicht aber in die TNA "verschoben".

Besser ist daher die Bearbeitung der Nachricht "WM_SIZE", deren wParam den Wert "SIZE_MINIMIZED" enthält, wenn das Fenster minimiert wurde. Und diesmal funktioniert es auch mit den angesprochenen System-Hotkey:

WM_SIZE:
  if(wp = SIZE_MINIMIZED) then
  begin
    if(Shell_NotifyIcon(NIM_ADD,@NID)) then
      ShowWindow(wnd,SW_HIDE);
  end else
    Result := DefWindowProc(wnd,uMsg,wp,lp);

Zu beachten ist allerdings, dass die Nachricht "WM_SIZE" auch ausgelöst wird, wenn das Fenster maximiert oder anderweitig in seiner Größe verändert wird. Darum ja auch die Einschränkung im Code, mit der wir gezielt auf die Minimierung prüfen. Dennoch dürfen wir die die anderen Möglichkeiten nicht blockieren und müssen Sie deshalb an die Standardfensterprozedur "DefWindowProc" weiterleiten.

Wie dem auch sei, das Programm (s. Beispiel "OnMinimize.dpr") weist nun das gewünschte Verhalten auf und minimiert sich in die TNA.


Die VCL-Version vs. Delphi 5

Wollen Sie das gleiche Verhalten für Ihre VCL-Programme verwenden, dann liegt es auf der Hand das obige Verhalten entsprechend nachzubilden. Sie müssen also auch hier auf die Nachricht "WM_SIZE" reagieren, was beispielsweise so aussehen kann:

type
  TForm1 = class(TForm)
    { ... }
  private
    procedure WMSize(var Message: TWMSize); message WM_SIZE;
  end;

In dieser privaten Prozedur erstellen Sie ebenfalls das TNA-Symbol und lassen den Button in der Startleiste verschwinden. Bei VCL-Programmen ist allerdings das TApplication-Objekt für diesen Button verantwortlich. Das Formular wird durch die Minimierung selbst bereits versteckt, so dass Sie hier also das Handle von TApplication benutzen müssen:

procedure TForm1.WMSize(var Message: TWMSize);
begin
  if(Message.Msg = WM_SIZE) and
    (Message.SizeType = SIZE_MINIMIZED) then
  begin
    if(Shell_NotifyIcon(NIM_ADD,@NID)) then
      ShowWindow(Application.Handle,SW_HIDE)
    else
      inherited;
  end else
    inherited;
end;


Eine weitere Möglichkeit wäre, direkt in die "WndProc" des Formulars einzugreifen und dort die o.g. Nachricht zu bearbeiten, etwa:

type
  TForm1 = class(TForm)
    { ... }
  protected
    procedure WndProc(var Message: TMessage); override;
  end;

Hierbei müssen Sie allerdings beachten, dass diese Prozedur wie in einem nonVCL-Programm der zentrale Anlaufpunkt für alle Arten von Nachrichten ist. Sie dürfen also keine blockieren, sondern Sie dürfen nur die gewünschte bearbeiten und müssen alle anderen weiterleiten.
Dabei kommt es auf die Art und Weise an, wie Sie das tun. In meinem Beispiel wird die Standardprozedur am Ende generell aufgerufen, so dass bei einer evtl. Nachrichtenbearbeitung die Prozedur vorher mit "exit" verlassen werden sollte:

procedure TForm1.WndProc(var Message: TMessage);
begin
  case Message.Msg of
    WM_SIZE:
      if(Message.wParam = SIZE_MINIMIZED) then begin
        if(Shell_NotifyIcon(NIM_ADD,@NID)) then begin
          ShowWindow(Application.Handle,SW_HIDE)
          exit;
        end;
      end;
{ ... } end;
inherited WndProc(Message); end;

So weit die Theorie. In der Praxis scheiterten beide Versuche bei mir (Delphi 5 Pro) allerdings daran, dass SIZE_MINIMIZED ignoriert wird. Warum auch immer ...

Wenn Sie das Flag testweise durch sein Gegenstück SIZE_MAXIMIZED (= die Anwendung wurde maximiert) ersetzen, dann erscheint das TNA-Symbol, und der Button in der Startleiste verschwindet. Wie gesagt: theoretisch sollte es also auch beim Minimieren funktionieren ...
Möglicherweise handelt es sich hierbei aber auch "nur" um einen Bug von Delphi 5. Vielleicht ist das in Ihrer Delphi-Version auch anders bzw. (falls es ein Bug ist:) behoben, so dass Sie obige Anregungen ausprobieren können.

Jedenfalls wollte ich Ihnen den Vorschlag nicht vorenthalten, auch wenn er bei mir nicht funktioniert.