¿Cómo iniciar una aplicación como un ícono en la bandeja del sistema (en la derecha de la barra de tareas de Windows)?

Iniciando una aplicación como un icono en la bandeja del sistema

Copyright © 2000 Ernesto De Spirito

DescargaBájese el código fuente

Boletín Pascal. Newsletter gratuito para programadores Delphi (y Kylix) con artículos, noticias, trucos, componentes y enlaces a nuevo contenido Delphi en la red.

Colocando un icono en la bandeja del sistema

La bandeja del sistema es el área en la parte derecha de la barra de tareas donde las aplicaciones que corren en el "fondo" (background) pueden colocar sus iconos. Para poner un icono en la bandeja del sistema tiene que llamar a la API Shell_NotifyIcon declarada en la unidad ShellAPI, pasándole la constante NIM_ADD (para decirle que agregue un icono) y la dirección de un registro TNotifyIconData con la información del icono que incluye el manejador del icono a mostrar, el texto a usar como ayuda de herramienta (tool tip) cuando el ratón está sobre el icono, el manejador de la ventana que recibirá los mensajes del icono y el tipo de los mensajes que el icono enviará a esta ventana.

uses
   ..., ShellAPI;

const
  WM_ICONTRAY = WM_USER + 1;   // Mensaje definido por el usuario

type
  ...

var
  ...
  NotifyIconData : TNotifyIconData;

implementation

procedure TForm1.FormCreate(Sender: TObject);
begin
  with NotifyIconData do begin
    hIcon := Icon.Handle;
    StrPCopy(szTip, Application.Title);
    Wnd := Handle;
    uCallbackMessage := WM_ICONTRAY;
    uID := 1;
    uFlags := NIF_MESSAGE + NIF_ICON + NIF_TIP;
    cbSize := sizeof(TNotifyIconData);
  end;
  Shell_NotifyIcon(NIM_ADD, @NotifyIconData);
end;

Manejando mensajes del icono

Form1 recibirá mensajes WM_ICONTRAY del icono. Para capturar y manejar estos mensajes tenemos que declarar un nuevo método en nuestro formulario. Por ejemplo:

type
  TForm1 = class(TForm)
    ...
  private
    { Private declarations  }
    ...
    procedure Icontray(var Msg: TMessage); message WM_ICONTRAY;
  public
    { Public declarations  }
    ...
  end;

En la implementación de este método puede por ejemplo mostrar un menú emergente (popup) cuando el usuario haga clic con el botón derecho sobre el icono. Asumiendo que ha creado un menú emergente llamado PopupMenu1, el siguiente código hará el truco:

procedure TForm1.Icontray(var Msg: TMessage);
var
  CursorPos : TPoint;
begin
  if Msg.lParam = WM_RBUTTONDOWN then begin
    GetCursorPos(CursorPos);
    SetForegroundWindow(Handle);        // sugerido por Berend Radstaat
    PopupMenu1.Popup(CursorPos.x, CursorPos.y);
    PostMessage(Handle, WM_NULL, 0, 0); // sugerido por Berend Radstaat
  end else
    inherited;
end;

Gracias a Berend Radstaat por las adiciones a este artículo.

Mostrando y ocultando el formulario

Normalmente usted incluirá un elemento de menú en el menú emergente para mostrar el formulario, así que todo lo que tiene que hacer es escribir un manejador de evento para llamar al método Show del formulario y opcionalmente remover el icono de la bandeja del sistema:

procedure TForm1.mnuMostrarClick(Sender: TObject);
begin
  Show;
  // Shell_NotifyIcon(NIM_DELETE, @NotifyIconData);
end;

Cuando el usuario cierra el formulario, usted puede por ejemplo ocultarlo en vez de cerrarlo y opcionalmente volver a mostrar el icono en la bandeja del sistema si es que lo ha removido cuando hizo el formulario visible. Por ejemplo:

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caNone;
  Hide;
  // Shell_NotifyIcon(NIM_ADD, @NotifyIconData);
end;

Terminando la aplicación

Puede tener un elemento de menú en el menú emergente para salir de la aplicación. En este caso lo que tiene que hacer es escribir un manejador de evento para remover el icono y finalizar la aplicación:

procedure TForm1.mnuSalirClick(Sender: TObject);
begin
  Shell_NotifyIcon(NIM_DELETE, @NotifyIconData);
  Application.ProcessMessages;
  Application.Terminate;
end;
JfControls Library - para Delphi y C++ Builder