Ask the Delphi Pro 10-Minute Solutions

Creating a System Tray Application
By Brendan Delumpa

Isn't it funny how coding certain things in Windows that appear as if they should be easy to implement, ultimately become exercises in "hair pulling"? It's not so much that the task of coding is that difficult; the information you need to accomplish it is either hidden behind layers of related, hyperlinked material (for example, Windows help) or does not exist in the first place (certain "omissions" in the Delphi technical documentation. Have I been here before?). Well, such is the case with creating System Tray applications. Implementation is actually a snap, but getting to the point where you know enough to implement it is not so straightforward. And when you see the code for creating a System Tray application, like me, you'll probably smirk. But don't worry, it's not something that'll make you say, "Duh!" Let's take a look...

There are two distinct things that you need to take care of to successfully create a System Tray application. The first thing is to "hide" your application from Windows. While System Tray applications look and act the same as any Windows program, they can't be switched to using Alt-Tab, nor do they have a button on the task bar. So we'll take care of that first.

By convention, any window that has a style of WS_EX_TOOLWINDOW is not available to the task bar, nor are users able to switch to it. At first, you might think that you would set this style using the CreateParams procedure. Unfortunately, that procedure will only work on a form. Now let's make a distinction here. The main form of the application is not the application's window. An application object has its own window--you can't see it, but it's there just the same. This is the window to which you apply the WS_EX_TOOLWINDOW style. So where do you put the code? In the project source, of course. So, open up an new project, then select View|Project Source from the main menu in the IDE. Once there, copy and paste the following code into the editor window.


program Project1;

uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1},
  Windows; //This is required to test the window style

{$R *.RES}

//Declare a var to retrieve current window information
var
  ExtendedStyle : Integer;

begin
  Application.Initialize;

  //Get the Extended Styles of the Application, by passing its
  //handle to GetWindowLong
  ExtendedStyle := GetWindowLong(Application.Handle, GWL_EXSTYLE);

  //Now, set the Extended Style by doing a bit masking operation.
  //OR in the WS_EX_TOOLWINDOW bit, and AND out the WS_EXAPPWINDOW bit
  //This effectively converts the application from an App Windows to a
  //Tool Window.
  SetWindowLong(Application.Handle, GWL_EXSTYLE, ExtendedStyle OR WS_EX_TOOLWINDOW
                                                 AND NOT WS_EX_APPWINDOW);
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.
Now, to actually create the effect of a System Tray application (put our application into the System Tray itself) is a bit more involved than hiding the application, but by no means is it a difficult proposition. So what will you need? First of all you'll need the main form of the application. Once there, drop a TPopupMenu component on the form. The popup menu will act as the main interface for performing various actions you'd like to take with your System Tray application. Take a look at the code below:

This sets up the application to be a system tray application and is the main form for the application. It has a popup menu that will be used to display the main form, or close the application. And using the ShellAPI unit, we can then use a couple of calls to display the application's icon on the system tray, and make it respond to a right mouse click.


unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, 
  ShellAPI, ExtCtrls, Menus;

type
  TForm1 = class(TForm)
    PopupMenu1: TPopupMenu;
    ShowMainForm1: TMenuItem;
    N1: TMenuItem;
    ExitApplication1: TMenuItem;
    procedure FormCreate(Sender: TObject);
    procedure ShowMainForm1Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure ExitApplication1Click(Sender: TObject);
  private
    procedure WndProc(var Msg : TMessage); override;
  public
    IconNotifyData : TNotifyIconData;
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
  //Set the border icons to have only a system menu. This will
  //leave just the close button.
  BorderIcons := [biSystemMenu];
  //Now set up the IconNotifyData structure so that it receives
  //the window messages sent to the application and displays
  //the application's tips
  with IconNotifyData do begin
    hIcon := Application.Icon.Handle;
    uCallbackMessage := WM_USER + 1;
    cbSize := sizeof(IconNotifyData);
    Wnd := Handle;
    uID := 100;
    uFlags := NIF_MESSAGE + NIF_ICON + NIF_TIP;
  end;
  //Copy the Application's Title into the tip for the icon
  StrPCopy(IconNotifyData.szTip, Application.Title);
  //Add the Icon to the system tray and use the
  //the structure and its values
  Shell_NotifyIcon(NIM_ADD, @IconNotifyData);
end;

procedure TForm1.WndProc(var Msg : TMessage);
var
  p : TPoint;
begin
  case Msg.Msg of
    WM_USER + 1:
    case Msg.lParam of
      WM_RBUTTONDOWN: begin
                        GetCursorPos(p);
                        PopupMenu1.Popup(p.x, p.y);
                      end;
    end;
  end;
  inherited;
end;

{This is one of the popup menu item's OnClick handler}
procedure TForm1.ShowMainForm1Click(Sender: TObject);
begin
  Form1.Show;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caNone;
  Form1.Hide;
end;

procedure TForm1.ExitApplication1Click(Sender: TObject);
begin
  Shell_NotifyIcon(NIM_DELETE, @IconNotifyData);
  Application.ProcessMessages;
  Application.Terminate;
end;

end.
As you can see, there's really not much to this. Just study the code to see what's going on. But the important thing you should concentrate on is the Create method of the form and what is done to the IconNotifyData structure. This is a record structure declared in the ShellAPI unit that stores information for a tray icon. Notice the flags that were used: NIF_MESSAGE + NIF_ICON + NIF_TIP. These flags tell the icon to process application messages, use the application's icon and its tip, respectively. Once we've set up all that, then it's a simple matter of creating the window interaction stuff, like we'd normally do at design time. The tray icon doesn't come into play until run time.

The other thing to look at is the override of the WndProc procedure. WndProc is short for Window Procedure. It intercepts all the messages sent to the window, and acts as the central message dispatcher. In that procedure, you can trap specific Windows messages by overriding the inherited procedure. In our case, we trap two things: the Msg field of the message sent to the application. If it was our custom message (WM_USER + 1) defined for the IconNotifyData variable, then we want to handle a right-click. All other messages sent to the application are handled in their normal fashion.

I realize that this 10-Minute Solution was pretty quick and dirty, so I encourage you to play around with the code. Just keep in mind that you have to do two things, if you're going to create system tray application:

  1. You need to first create the "hiding" mechanism for the application.
  2. Then, you need to create the interface so that you can interact with the application when its main form isn't being displayed.
Notice that I didn't really do too much with the form. I wanted to keep the presentation as simple as possible. However, there is nothing to stop you from making a more complex menu with more complicated actions. Remember: a System Tray application is pretty much like any other Windows program.
 
Other 10-Minute Solutions
 Trapping Messages Sent to an Application
 Getting the Number of Records From a Fixed-Length ASCII File
 Performing Incremental Searches with a TListbox
 Resizing the Drop-down List of a TComboBo
 Disabling the System Keys from Your Application
 Making A Secondary Form Independent of the Main Form
 Creating a System Tray Application
 Opening and Closing a CD Tray
 Hiding an Application from Windows
 Make a List Box Track the Mouse
 Running a Program at Startup
 Creating A Skin Component
 Dynamically Load Components From Packages at Run Time
 Obtain All Values from Multivalue Input Fields in WebBroker Applications
 Migrate Your BDE Applications to Linux with dbExpress
 Create a Multiline Button Component
 Update and Maintain dbExpress's Unidirectional, Read-Only Datasets
 Use ActionBands to Enable End Users to Customize Your Delphi Applications
 Produce HTML Reporting Output with WebBroker Components
  Convert XML Documents into Different Formats with the XSL Template Language


Ask the Delphi Pro | Who is the Pro? | Usage Policies | Ask a Question | Search | Feedback


Sponsored Links


Advertising Info  |   Member Services  |   Contact Us  |   Help  |   Feedback  |   Site Map
Jupiterweb networks

internet.comearthweb.comDevx.comClickZ

Search Jupiterweb:

Jupitermedia Corporation has four divisions:
JupiterWeb, JupiterResearch, JupiterEvents, and JupiterImages

Copyright 2004 Jupitermedia Corporation All Rights Reserved.
Legal Notices, Licensing, Reprints, & Permissions, Privacy Policy.

Jupitermedia Corporate Info | Newsletters | Tech Jobs | E-mail Offers