| 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
- MSDN on Microsoft
- Well if it is windows programming, this is a must. A very very
comprehensive and exhaustive guide on the subject.
- Programming Windows - A good first book for windows programming by Charles
Petzold.
- Programming windows 95 with MFC - Another wonderful book by Jeff
Prosise.
Author : Manish Hatwalne.