Placing Delphi Applications in the System Tray

The Perfect Place for Programs Left Running with No User Interaction

Businesswoman working on project on computer
Thomas Barwick/Stone/Getty Images

Take a look at your Task Bar. See the area where the time is located? Are there any other icons there? The place is called the Windows System Tray. Would you like to place your Delphi application's icon there? Would you like that icon to be animated - or reflect the state of your application?

This would be useful for programs that are left running for long periods of time with no user interaction (background tasks you typically keep running on your PC all day long).

What you can do is to make your Delphi applications look as if they are minimizing to the Tray (instead to the Task Bar - right to the Win Start button) by placing an icon in the tray and simultaneously making your form(s) invisible.

Let's Tray It

Fortunately, creating an application that runs in the system tray is pretty easy - only one (API) function, Shell_NotifyIcon, is needed to accomplish the task.

The function is defined in the ShellAPI unit and requires two parameters. The first is a flag indicating whether the icon is being added, modified, or removed, and the second is a pointer to a TNotifyIconData structure holding the information about the icon. That includes the handle of the icon to show, the text to show as tool tip when the mouse is over the icon, the handle of the window that will receive the messages of the icon and the message type the icon will send to this window.

First, in your main form's Private section put the line: 
TrayIconData: TNotifyIconData;

  TMainForm = class(TForm)
    procedure FormCreate(Sender: TObject);
    TrayIconData: TNotifyIconData;
    { Private declarations }
    { Public declarations }

Then, in your main form's OnCreate method, initialize the TrayIconData data structure and call the Shell_NotifyIcon function:

with TrayIconData do
    cbSize := SizeOf(TrayIconData);
    Wnd := Handle;
    uID := 0;
    uCallbackMessage := WM_ICONTRAY;
    hIcon := Application.Icon.Handle;
    StrPCopy(szTip, Application.Title);

  Shell_NotifyIcon(NIM_ADD, @TrayIconData);

The Wnd parameter of the TrayIconData structure points to the window that receives notification messages associated with an icon. 

The hIcon points to the icon we want to ad to the Tray - in this case Applications main icon is used. 
The szTip holds the Tooltip text to display for the icon - in our case the title of the application. The szTip can hold up to 64 characters.

The uFlags parameter is set to tell the icon to process application messages, use the application's icon and its tip. The uCallbackMessage points to the application defined message identifier. The system uses the specified identifier for notification messages that it sends to the window identified by Wnd whenever a mouse event occurs in the bounding rectangle of the icon. This parameter is set to WM_ICONTRAY constant defined in the interface section of the forms unit and equals: WM_USER + 1;

You add the icon to the Tray by calling the Shell_NotifyIcon API function.

The first parameter "NIM_ADD" adds an icon to the Tray area. The other two possible values, NIM_DELETE and NIM_MODIFY are used to delete or modify an icon in the Tray - we'll see how later in this article. The second parameter we send to the Shell_NotifyIcon is the initialized TrayIconData structure.

Take one...

If you RUN your project now you'll see an icon near the Clock in the Tray. Note three things. 

1) First, nothing happens when you click (or do anything else with the mouse) on the icon placed in the Tray - we haven't created a procedure (message handler), yet. 
2) Second, there is a button on the Task Bar (we obviously don't want it there). 
3) Third, when you close your application, the icon remains in the Tray.

Take two...

Let's solve this backward. To have the icon removed from the Tray when you exit the application, you have to call the Shell_NotifyIcon again, but with the NIM_DELETE as the first parameter.

You do this in the OnDestroy event handler for the Main form.

procedure TMainForm.FormDestroy(Sender: TObject);
  Shell_NotifyIcon(NIM_DELETE, @TrayIconData);

To hide the application (application's button) from the Task Bar we'll use a simple trick. In the Projects source code add the following line: Application.ShowMainForm := False; before the Application.CreateForm(TMainForm, MainForm); E.g let it look like:

  Application.ShowMainForm := False;
  Application.CreateForm(TMainForm, MainForm);

And finally to have our Tray icon respond to mouse events, we need to create a message handling procedure. First we declare a message handling procedure in the public part of the form declaration: procedure TrayMessage(var Msg: TMessage); message WM_ICONTRAY; Second the definition of this procedure looks like:

procedure TMainForm.TrayMessage(var Msg: TMessage);
  case Msg.lParam of
      ShowMessage('Left button clicked 
                  - let''s SHOW the Form!');
      ShowMessage('Right button clicked 
                  - let''s HIDE the Form!');

This procedure is designed to handle only our message, the WM_ICONTRAY. It takes the LParam value from the message structure which can give us the state of the mouse upon the activation of the procedure. For the sake of simplicity we'll handle only left mouse down (WM_LBUTTONDOWN) and right mouse down (WM_RBUTTONDOWN). When the left mouse button is down on the icon we show the main form, when the right button is pressed we hide it. Of course there are other mouse input messages you can handle in the procedure, like, button up, button double click etc.

That's it. Quick and easy. Next, you'll see how to animate the icon in the Tray and how to have that icon reflect the state of your application. Even more, you'll see how to display a pop up menu near the icon.