Introduction
You must have seen the messengers or anti-virus icons which sit in your windows system tray (right hand bottom corner) and application UI launches when you double-click it. kind of  "cool", isn't it? The article discusses how do you go about doing it using windows SDK APIs (Platform SDK). However, you need to be already familiar with windows programming using platform SDK to do this.

Objective : To add an icon in the system tray which represents an application and handles the messages specific to that application. It will display the corresponding application window (UI) when you double-click on it, and right click will show the menu. 

How to do it : At times it is required that one adds the icon representing the application in the system tray so that the user can double-click and start the application UI window and right click would enable the user to select a desired option from the pop-up menu. Also, it may be desired that the application should continue its normal functioning in the background and only the icon is visible in the tray and unless explicitly invoked there should be no GUI visible for the application. 

One example of such an application could be a scheduler which resides in the system tray and launches a desired application at the scheduled time. To set date, time and other details of this scheduler, the user may double-click the icon in the system tray and set the required details. Otherwise, by default  the scheduler is working in the background and determines if it is the time to launch any application and launches it accordingly. 
This is achieved using the function given below :- 

 

1) Shell_NotifyIcon()

WINSHELLAPI BOOL WINAPI Shell_NotifyIcon( DWORD dwMessage, PNOTIFYICONDATA pnid );

Sends a message to the system to add, modify, or delete an icon from the taskbar status area. 

dwMessage 
First parameter is the message value to send. This parameter can be one of these values: 
NIM_ADD 
Adds an icon to the status area. (System Tray) 

NIM_DELETE 
Deletes an icon from the status area. (System Tray) 

NIM_MODIFY 
Modifies an icon in the status area. (System Tray) 

pnid 
Address of a NOTIFYICONDATA structure. The content of the structure depends on the value of dwMessage, the first parameter of the function call. 

2 ) NOTIFYICONDATA structure

typedef struct _NOTIFYICONDATA {
			   DWORD cbSize;
			   HWND hWnd;
			   UINT uID;
			   UINT uFlags;
			   UINT uCallbackMessage;
			   HICON hIcon;
			   char szTip[64];
} NOTIFYICONDATA, *PNOTIFYICONDATA;

It contains information that the system needs to process taskbar status area (System Tray) messages. 

cbSize
Size of this NOTIFYICONDATA structure, in bytes. Usually value given by the sizeof operator. 
hWnd 
Handle to the window that will receive notification messages associated with an icon in the taskbar status(system tray) area. This window?s procedure will handle all the messages meant for that application. 
uID 
Application-defined identifier of the taskbar icon. (The ID of the icon defined in the resource file) 
uFlags 
Array of flags that indicate which of the other members contain valid data. This member can be a combination of the following which can be done by ORing the values below:  (OR operator)
NIF_ICON 
The hIcon member is valid. I.e this parameter will be supplied. 
NIF_MESSAGE 
The uCallbackMessage member is valid. 
NIF_TIP 
The szTip member is valid. 
uCallbackMessage 
Application-defined message identifier. The system uses this identifier for notification messages that it sends to the window identified in hWnd. These notifications are sent when a mouse event occurs in the bounding rectangle of the icon. This is the user defined message (like WM_USER_SHELLICON) which will be called by the system. 
hIcon 
Handle to the icon to add, modify, or delete.This is the handle of the icon obtained from LoadIcon() call. 
szTip 
Tooltip text to display for the icon. Displayed when mouse is placed over the bounding rectangle of the Icon. 

3 ) Procedure

Part 1 : Before You Start WinMain() ...

Create an Icon in the resource editor (You can experiment with your graphics skills here ;-)) 
Define a user defined message as a new uCallbackMessage in the header file as shown below 
WM_USER_SHELLICON WM_USER + 1 

Part 2 : Inside WinMain()

(1) Inside WinMain() after declaring message struct, hInstance and other details load the icon

HICON hMainIcon = LoadIcon(hInstance,(LPCTSTR)MAKEINTRESOURCE(IDI_TRAYICON));
	  			//for modify
HICON hMainIcon1 = LoadIcon(hInstance,(LPCTSTR)MAKEINTRESOURCE(IDI_TRAYICON_ACTIVE)); 

(2) Fill the NOTIFYICONDATA structure with the required info. ( as explained above) 

	 // sizeof the struct in bytes
structNID.cbSize = sizeof(NOTIFYICONDATA);
	//handle of the window which will process this app. messages
structNID.hWnd = hWnd;
	//ID of the icon that willl appear in the system tray
structNID.uID = IDI_TRAYICON;
	//ORing of all the flags
structNID.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
	//Text of the tooltip
strcpy(structNID.szTip,"Test Of Shell Notify Icon");
	// handle of the Icon to be displayed, obtained from LoadIcon
structNID.hIcon = hMainIcon;
	// user defined message that will be sent as
	// the notification message to the Window Procedure
structNID.uCallbackMessage = WM_USER_SHELLICON;


(3) Call the actual function Shell_NotifyIcon(..) with NIM_ADD and this structure as parameters. 
Shell_NotifyIcon(NIM_ADD, &structNID); 

Part 3 : Inside WndProc() (Or the proc where the messages will be handled)

(1) Process the user defined message (WM_USER_SHELLICON in this case) as one of the messages in the WndProc. 

This message will be sent to the application whenever the focus is on the specific icon . All the cases specific to that icon must be handled inside this message. LOWORD of lParam will have the message ID for that icon. This is shown in the code snippet below - 

case WM_USER_SHELLICON:
    switch(LOWORD(lParam))
    {
        case WM_LBUTTONDBLCLK: // to open the GUI of the Application
            ShowWindow(hWnd, SW_NORMAL);
            return TRUE;
        case WM_RBUTTONDOWN:
                //get mouse cursor position x and y as lParam has the message itself
            GetCurrentPositionEx(hdc,&lpClickPoint);
                //place the window/menu there if needed
            //............

            MessageBox(NULL,"TEST rightclick","Testing ...",MB_OK);
                //can diplay menu here instead of the MessageBox

            return TRUE;
    }
    break;

Note that the mouse co-ordinates(x,y) are usually available in the lParam of the message, but in this case the message itself is passed as the LOWORD of the lParam parameter, hence an explicit call to GetCurrentPositionEx(..) must be made to get the co-ordinates if it is so desired. 

(2) When the GUI for the application is minimized the application again resides in the system tray in the icon format if so desired. This typically can be handled in WM_SYSCOMMAND message of the Windows Procedure. (WndProc()) 

case WM_SYSCOMMAND:
    if(SC_MINIMIZE == wParam)
    {
        ShowWindow(hWnd,SW_HIDE); //hWnd is the handle of the application window
        return TRUE;
    }
    break;

(3) When the application is closed / exited the icon from the system tray must be removed. This is done by passing NIM_DELETE as the first parameter to the Shell_NotifyIcon function. This typically can be handled in the WM_CLOSE message of the Windows Procedure 

case WM_CLOSE:
        // remove the icon from taskbar (system tray) and Post a WM_QUIT message
    Shell_NotifyIcon(NIM_DELETE,&structNID);
    DestroyWindow(hWnd);
    PostQuitMessage(0);
    break;

(4) If any of the parameters in the NOTIFYICONDATA are changed (like changing the icon itself or changing the toolotip text) this can be reflected by the Shell_NotifyIcon function by passing NIM_MODIFY as the first parameter and the modified struct as the second parameter. This can be processed in any suitable message (eg. Icon and the tooltip text can be changed when the app is running with its User Interface) 

//suitable message body
//.............................

structNID.cbSize = sizeof(NOTIFYICONDATA);
structNID.hWnd = hWnd;
structNID.uID = IDI_TRAYICON_ACTIVE; // icon is changed when UI is visible
structNID.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
strcpy(structNID.szTip,"Application is running"); // text is changed 
structNID.hIcon = hMainIcon1; // handle of the changed icon
structNID.uCallbackMessage = WM_USER_SHELLICON;
Shell_NotifyIcon(NIM_MODIFY,&structNID);

//.............................

In the end

You can process all the other messages in the same proc. using the usual procedure for handling them. The normal application code can be handled in the relevant messages like any other SDK application program. Note that the messages inside the WM_USER_SHELLICON are different from the message outside it. Hence you can have all other window messages like WM_LBUTTONDBLCLK and WM_RBUTTONDOWN as case options to the WM_USER_SHELLICON message. 
For that matter, any application which displays icon in the system tray and uses the Shell_NotifyIcon call for the same, is like any other SDK application; hence rest of the procedure for handling messages and processing accordingly remains the same. It just additionally processes the messages and function calls mentioned above.

Demo Application

Surprisingly, many people seem to be interested in this topic. A few of them have been motivated enough to send me a mail and ask for the code. I didn't know that so many people would be interested in doing this using platform SDK, given popularity of the tools like MFC/VB, and ease with which they let you do these things.
I have put up a demo "do nothing" Shell_NoifyIcon application for all those who are interested. The application displays an icon in the system tray, right clicking on which displays a pop up menu, and double clicking opens the application UI, which doesn't do anything. The code is not very well documented, but it should provide all the required framework and guidelines nevertheless. The application executable is zipped along with the source code and icon in the snicon.zip file which you can download and use. The code has been tested on Windows 2000 and should work with other win 32 systems as well. Do let me know if it helps, and as usual the standard disclaimer applies. Happy coding and debugging ;))

[ Download Shell Notify Icon Demo Application (17 KB) ]


Links, Further Reading & References
Author : Manish Hatwalne.