Site hosted by Angelfire.com: Build your free website today!

Home
DelphiZeus
Tray Notify Icon
Put an Icon in the Task Bar Status Area

Home



Many programs will display a small Icon on the right side of a horizontal Taskbar (the status area with the clock), which is called a Tray icon. These tray icons are intended to be used as a Status Indicator for non-visible programs or lengthty processes like printing. The user can move his mouse over the small Icon to get a Tip, or the user can right click and get a pop-up menu, or left click to show a window or dialog box. The Shell_NotifyIcon( ) function is used to place a tray icon in the task bar status area. Once the tray icon is shown, your program will get notification messages about mouse activity in the area of the tray icon. You can use these messages to show menus, show windows, stop a process, or exit your program. You must always delete any Tray Icon that you have created, because the system does not automaticly delete it.

Old and New Tray Icon Versions
In the windows 2000 OS the tray icon was given more functionality, it added the "Ballon" tray icon Tip, so the system NOTIFYICONDATA structure in API was give more members for this "Ballon" tip. I will Not have anything in this lesson about the newer tray icon with ballon tips. The code here will be for the API of the windows 95 version, so all of the code here will work in all 32 bit versions of windows.

The Shell_NotifyIcon( ) function
You can use the Shell_NotifyIcon( ) function to add, change, or delete icons from the task bar status area. The 2 parameters for Shell_NotifyIcon( ) include the flag for the dwMessage to send, and a PNotifyIconData record pointer. The TNotifyIconData record contains information that the system uses to create, change or delete a tray icon. The Shell_NotifyIcon function is defined in the ShellApi.pas as -

function Shell_NotifyIcon(
           dwMessage: Cardinal;  // message identifier 
           lpData: PNotifyIconData  // pointer to TNotifyIconData
           ): Boolean;
There are three dwMessage flags, NIM_ADD, to add an Icon to the task bar, NIM_MODIFY, to change the Icon, and NIM_DELETE, to delete an Icon from the task bar. You can only use ONE of these messages in a call to Shell_NotifyIcon.

The next parameter, PNotifyIconData is defined in the shellapi.pas as -
  PNotifyIconDataA = ^TNotifyIconDataA;
  PNotifyIconData = PNotifyIconDataA;
  _NOTIFYICONDATAA = record
    cbSize: Cardinal;
    Wnd: HWND;
    uID: Cardinal;
    uFlags: Cardinal;
    uCallbackMessage: Cardinal;
    hIcon: HICON;
    szTip: array [0..63] of AnsiChar;
  end;
  TNotifyIconDataA = _NOTIFYICONDATAA;
  TNotifyIconData = TNotifyIconDataA;


The members of the TNotifyIconData record

cbSize - Size of the TNotifyIconData record used, set to SizeOf(TNotifyIconData) for old style tray icon.

hWnd - Handle of the window that receives mouse event notification messages from a tray icon in the taskbar status area.

uID - the ID number of the taskbar icon, used if there is more than one tray icon for your program.

uFlags - Flag bits that indicate which of the other record members contain data that will be used by the Shell_NotifyIcon function. The uFlags can be a combination of these values:
  NIF_ICON - The hIcon member is used.
  NIF_MESSAGE - The uCallbackMessage member is used.
  NIF_TIP - The szTip member is used.

uCallbackMessage - Your windows message number, which must be in the range of WM_USER messages. The system uses this message for notification that it sends to the window identified by hWnd whenever a mouse event occurs in the bounding rectangle of the tray icon.

hIcon - Handle of the icon used in the tray.

szTip - Tooltip text to display for the icon, which can contain up to 63 characters.



An example of code used to place a Tray Icon in the Task bar -
var
IconData: TNotifyIconData;

begin
IconData.cbSize := SizeOf(TNotifyIconData);
IconData.Wnd := hForm1;
IconData.uID := 12;
IconData.uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP;
IconData.uCallbackMessage := WM_USER + 144;
IconData.hIcon := hMyIcon;
IconData.szTip := 'a Tray Icon Tip';

Shell_NotifyIcon(NIM_ADD, @IconData);
end;
IMPORTANT, You must DELETE the Tray Icon
Once a Tray Icon is placed in the Task bar, it is not associated with your program except that it sends notification messages to it. So if you exit your program the Tray Icon will still be in the task bar, it is NOT removed or deleted with your program's closing. (although in newer systems there seems to be some checking for a windows existance). ALWAYS have a call to delete the tray icon that you have created, like this -
Shell_NotifyIcon(NIM_DELETE, @IconData);
The IconData must have two members set, the IconData.Wnd and the IconData.uID as the values you created the tray icon with.



The uCallbackMessage Notification
The window whose handle is placed in the IconData.Wnd above, will recieve the mouse events as a message with the value given in the IconData.uCallbackMessage. You must use a message number in the WM_USER range, which is WM_USER up to WM_USER + 32767. This message will go to the Window Proc of that window. See the WM_TRAYICONCLICKED message of the function MessageFunc( ) in the Tray Icon Program code below. All of the mouse (client area click) messages are sent, these include -
WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK, WM_RBUTTONUP, WM_MBUTTONUP, WM_MBUTTONDOWN
and the other right and middle button click messages. The WM_MOUSEMOVE is also sent.

The WM_MOUSEWHEEL message is NOT processed and passed to your program, so you will not get any mouse wheel activity tray icon messages.



Pop Up Menu Creation
In the TrayIcon program code below, I have the WM_TRAYICONCLICKED message for the WM_RBUTTONUP event show a Pop-Up menu, as most tray icons do. I have the ShowPopMenu( ) procedure for the code that makes this pop-up menu. You can see more about using API menus here at DelphiZeus in Lesson 10 - Menus and List Boxes. There is one addition to the code you find there for pop-up menus used here. Since the system tray with your icon in it does not get focus when you click it, your pop-up menu may not close if you click on something else. If you add the code -
  SetForegroundWindow(hForm1);
so something has focus, it seems to solve this problem.



A Note About Tray Icons
Please try and think about how your tray icon will Help (improve or quicken operation) for your user. You should have your Tray Icon as a "Status" indicator for your program, this means you should change the Icon, or at least the "Tip" as your program does things. Also, I beleive it is a misteak to think that your user will want your tray icon. Tray Icons are now widely used by many popular programs, (volume-control, printer, eMail, anti-virus, messenger, music, system). I have seen people's computers with more than 20 Tray Icons, so many tray icons that the user did NOT know what most of the icons were for, and NEVER-EVER used some of these icons (did not ever click them or look at them). The user wanted to "Get Rid" (delete or not show) many of the tray icons they did not know and did not use, but many programs did not offer that option (no tray icon) or you had to open up the it's main program and search through several "Operation" windows and find a window with a menu that had an "Options" for tray Icons, and try to find the no-show tray icon option. If possible have your "Tray Icon Options" in your tray icon pop-up menu. If possible ask your user if they want your icon in their tray, before you place it there, or at least offer them a way to "Not Show" your icon in their tray.




Tray Icon Program

This program will create an invisible (hidden) Main Form window using the WS_EX_TOOLWINDOW in the CreateWindowEX function. Even if you show this window it will not be seen because the width and height are set to Zero. If you include the WS_EX_TOOLWINDOW flag in the EX style then the window will not place a button in the Task bar when it is shown. A tray icon is created with Shell_NotifyIcon(NIM_ADD, @IconData);, if you look at the procedure Do101; and procedure Do102; you will see code to Modify the tray icon. A Pop-Up Menu is created and shown in the procedure ShowPopMenu( ) which is called for a right click on the tray icon. Since the ShellApi is in the uses clause, I have the ShellExecute( ) function in this program, which you can see in procedure Do102; and procedure Do103;.


program TrayIcon;
{this Program creates an Invisible window, and places a
Tray Icon on the Task bar. the Tray icon can be right
clicked for a menu}

uses
  Windows, Messages, ShellApi;
  {you need to include ShellApi in the uses for the Shell_NotifyIcon}

{$R *.RES}

var
wClass: TWndClass;
hForm1: Integer;
MainMsg: TMSG;
IconData: TNotifyIconData;

const
 Zero = 0;
 WM_TRAYICONCLICKED = WM_USER + 1776;
 {the tray icon clicked message has to be a WN_USER message}

procedure ShutDown;
begin
{be sure to Delete all the Tray Icons you create}
Shell_NotifyIcon(NIM_DELETE, @IconData);
PostQuitMessage(Zero);
end;

procedure ShowPopMenu(ShowPt: TPoint);
{this procedure creates and shows, and destroys an Popup menu}
var
hPopMenu: HMENU;
begin
hPopMenu := CreatePopupMenu;
{this CreatePopupMenu fuction makes an empty menu}

SetForegroundWindow(hForm1);
// Add this SetForegroundWindow to have this Pop-Up Menu
// close if you click something that is NOT this menu

{AppendMenu() adds Items to a menu}
AppendMenu(hPopMenu, MF_STRING, 101,'Show message');
{the  101  is the ID number sent to
the WndProc message function when menu click}
AppendMenu(hPopMenu, MF_STRING, 102, 'Open Notepad');
AppendMenu(hPopMenu, MF_STRING, 103, 'Open Yahoo web page');
AppendMenu(hPopMenu, MF_SEPARATOR, 0,nil);
AppendMenu(hPopMenu, MF_STRING, 104, 'Exit Tray Icon');

{the TrackPopupMenu fuction is active as long as the PopUp Menu is displayed,
so nothing else is happening in this thread while the menu is up}
TrackPopupMenu(hPopMenu,     // handle of shortcut menu
   TPM_LEFTALIGN or TPM_LEFTBUTTON,     // screen-position and mouse-button flags
   ShowPt.x-5,     // horizontal position, in screen coordinates
   ShowPt.y-5,     // vertical position, in screen coordinates
   Zero,     // reserved, must be zero
   hForm1,     // handle of window that gets menu messages
   { see  WndProc at the WM_COMMAND for menu messages}
   nil     // points to RECT that specifies no-dismissal area
  );
DestroyMenu(hPopMenu);
end;

procedure Do101;
begin
MessageBox(0,'the Do101 Message box, the Tray Icon Tip will be changed',
          'just a message box',MB_OK or MB_ICONINFORMATION);
IconData.uFlags := NIF_TIP;
IconData.szTip := 'New Tip, Click will get a Menu';
Shell_NotifyIcon(NIM_MODIFY, @IconData);
{the NIM_MODIFY flag tells the system to change the Tray Icon
according to the flags in the IconData.uFlags. Here the NIF_TIP
flag is set and a new szTip is given, so a new Tip will be seen}
end;

procedure Do102;
begin
IconData.uFlags := NIF_ICON;
IconData.hIcon := LoadIcon(0, IDI_HAND{IDI_QUESTION});
Shell_NotifyIcon(NIM_MODIFY, @IconData);
{NIM_MODIFY with the NIF_ICON flag will change the Tray Icon
to the System Error Icon given in the IconData.hIcon}
ShellExecute(hForm1, 'open', 'notepad.exe', nil, nil, SW_SHOWNORMAL);
end;

procedure Do103;
begin
ShellExecute(hForm1, 'open', 'http://www.yahoo.com', nil, nil, SW_SHOWNORMAL);
end;

function MessageFunc(hWnd, Msg, wParam, lParam:Integer):Integer; stdcall;
{this is the fuction that gets the window messages and test's the message
to see if code should be executed}
var
Pt1: TPoint;
begin
case Msg of
   {menu click messages are lParam = Zero}
  WM_COMMAND:
    if lParam = Zero then
      begin
      case LOWORD(wParam) of
        {the menu ID number is in the LOWORD position of wParam}
        101: Do101;
        102: Do102;
        103: Do103;
        104: PostMessage(hForm1,WM_CLOSE,Zero,Zero);
        {to end this program you post the WM_CLOSE message}
        end;
      end;

  WM_TRAYICONCLICKED: begin
{this is a user message used for a Tray Icon Mouse Message, you will
need to test for the mouse messages, The wParam has the ID number of the
Tray icon, which is used to separate the WM_TRAYICONCLICKED messages if
you have 2 or more Tray Icons for your program}
  if (lParam = WM_RBUTTONUP) then
    begin
    {this shows a Menu on a Tray Icon Right Click}
    GetCursorPos(Pt1);
    {GetCursorPos seemed to be an easy way to get where the mouse was clicked}
    ShowPopMenu(Pt1);
    end else
    if lParam = WM_LBUTTONUP then
    {this shows an Abot box on a Left Click}
    ShellAbout(0, 'About the Tray Icon#This is Version 1-1-1',
               'This is About the Tray Icon, it show''s how to use the Shell_NotifyIcon', 
                wClass.hIcon) else
       if lParam = WM_LBUTTONDBLCLK then
    PostMessage(hForm1,WM_CLOSE,Zero,Zero);
    {This Exits the program on a Double Click}
    end;
  WM_DESTROY: ShutDown;
  end; // case
Result:=DefWindowProc(hWnd,Msg,wParam,lParam);
end;

begin   // MAIN BEGIN / / / / / / / / / / / / / / / / / / / / / /
with wClass do
 begin
   Style:=         Zero;
   lpfnWndProc:=   @MessageFunc;
   hInstance:=     SysInit.HInstance;
   hbrBackground:= COLOR_BTNFACE+1;
   hIcon:=         LoadIcon(hInstance,'MAINICON');
   hCursor:=       LoadCursor(Zero,IDC_ARROW);
   lpszClassName:= 'First Class';
 end;

windows.RegisterClass(wClass);

hForm1 := CreateWindowEx(WS_EX_TOOLWINDOW, wClass.lpszClassName, 'NoSee',
         WS_POPUP,
         Zero, Zero, Zero, Zero, Zero, Zero, hInstance, nil);
{this hForm1 is created as a tool window and there is no WS_VISIBLE style bit,
so the window is hidden, even if you show the window, it will not be seen because the
height and width are Zero and it will not have a button on the tack bar because
it is a tool window}

{this IconData is used by the Shell_NotifyIcon to get that Tray Icon}
with IconData do begin
  cbSize := SizeOf(TNotifyIconData);
  {as with most windows records set the size to the record size}
  Wnd := hForm1;
  {Wnd is the window that will get the notify message}
  uID := 5;
  {I don't use the ID but I set it to 5, the ID is used
  if you create more than one Tray icon}
  uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP;
  {these flags indicate what will be set when
  the Shell_NotifyIcon is called, if you do not have
  the NIF_TIP, then no tip will be set}
  uCallbackMessage := WM_TRAYICONCLICKED;
  {set the callback message here, to be used in the MessageFunc}
  hIcon := wClass.hIcon;
  {wClass.hIcon is the MAINICON}
  szTip := 'Tray Icon, Right Click to get a Menu';
end;

Shell_NotifyIcon(NIM_ADD, @IconData);
{Shell_NotifyIcon with the NIM_ADD parameter will add a tray icon}

while GetMessage(MainMsg,Zero,Zero,Zero) do
 begin
 TranslateMessage(MainMsg);
 DispatchMessage(MainMsg);
 end;
end.

                           

Next
It's time to make a Text Editing program like Notepad, called TestText, it reads and writes to the registry.
  15. TestText Text Editor


       

Lesson -     One  Two  Three  Four  Five  Six  Seven  Eight  Nine  Ten  Eleven  Twelve




H O M E