Intro.
You must have noticed in Windows versions ranging from NT/2000 onwards, an Area near the System clock display, holding some icons. This is the Taskbar Notification Area. This is also sometimes erroneously called the "tray."
And, you have seen some Applications, mainly Anti-virus applications hiding their Window from display, and running in Background with a Icon in this Notification area. Well, we call it Tray Icon too. And, what if we need to create such an icon for our Application, and hide its window from normal display?
Ofcourse you can do it. Shell_NotifyIcon() is the API which helps us to do it beautifully :) See the above picture, sorry mousepointer cannot be captured by traditional screenshot, its that Red Heart Icon representing my application displaying a tooltip given by me.
How to do.
Well. Look at the code snip given below. :)
Code Snippet.
HMENU hMenu;
NOTIFYICONDATA NID; //The Structure containing info about our Notify Icon.
BOOL Minimized = false; //A Flag.. just to indicate our Window is minimized or not
Now, add these folowing code in your Main Window's InitInstance() After the line CreateWindow().
Note : Here i have used a Resource Identifier named IDI_ICON1. Its a Resource ID. You need to Add the Icon Resource to your Resource file, and name it IDI-ICON1 or whatever you want. :)
// Size of the structure duh :)
NID.cbSize = sizeof(NID);
// The icon that will be shown in the systray.
NID.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
// Our user defined message.(For notifications, Windows will send us messages when user pressed on our icon)
NID.uCallbackMessage = WM_USER + 1;
// Handle to the window we would like to get the notifications.
NID.hWnd = hWnd;
// Icon ID in systray...
NID.uID = WM_USER + 2;
// The tooltip string
strcpy(NID.szTip, "System Tray Icon[My App]");
// Here we say we want to get notifications about the icon, we have an icon and a tooltip.
NID.uFlags = NIF_TIP NIF_ICON NIF_MESSAGE;
hMenu = LoadMenu(hInstance, "menu1");
Now, we have the Structure filled in, and initialized. All we need to do is just Create the Icon on Tray. Wait... We dont simply need to create Icon. We need to Minimize our Application to Tray.. so, Wait for our Window to be Minimized.. What should we do then??.. Yeahh.. Capture the Window Minimizing event message. Here it is, Add to your Message handler.
case WM_SIZE:
{if (wParam == SIZE_MINIMIZED)
{
//Set Falg to Window minimized state - true
Minimized = true;
// Hide our window just after the minimize is done.
ShowWindow(hWnd, SW_HIDE);
//Add the systray icon.
Shell_NotifyIcon(NIM_ADD, &NID);
}
}break;
Lolzz.. its Done!.. Now, Run your code. The Window will display, Minimize it... yeah.. the Icon you proviided as IDI_ICON1 is now showing in the Notification Area.
But.. you cannot do anything with that Icon.. why?? Because we havent defined any actions to be performed on Events. Now, lets do some action with our new Notifier Icon.
I'm going to add some Menu.. yes, when the user right clicks, it should show a Menu popup(see example image below). We can use CreatePopupMenu() API to create Popup Menus.. and AppendMenu() to append Items to the Menu popuplist.
See how..
Add these following code to the end of your WndProc CallBack Function. And remember, you returns the Callback AFTER this block of code. eg : return DefWindowProc(hWnd, message, wParam, lParam);
This code comes outside of the switch(message) block and before the return DefWindowProc(hWnd, message, wParam, lParam);
//This is where we get our SysTray Icon notifications.
if (message == NID.uCallbackMessage)
if (message == NID.uCallbackMessage)
{
switch(lParam)
{
case WM_RBUTTONDOWN:
{
// Create an empty popup menu
HMENU popMenu = CreatePopupMenu();
// These add items to the menu
AppendMenu(popMenu, MF_STRING, ID_MENU_SHOW, "Show");
AppendMenu(popMenu, MF_STRING, /*This should be a ID to handle the menu item*/13, "Show2");
AppendMenu(popMenu, MF_STRING, /*This should be a ID to handle the menu item*/ID_MENU_EXIT, "Exit");
// Get the position of the cursor
POINT pCursor;
GetCursorPos(&pCursor);
// Popup the menu with cursor position as the coordinates to pop it up
TrackPopupMenu(popMenu, TPM_LEFTBUTTON TPM_RIGHTALIGN, pCursor.x, pCursor.y, 0, hWnd, NULL);
}break;
case WM_LBUTTONUP: //Did the user left click?
{ //Show the window and remove the systray icon.
ShowWindow(appWindow, SW_SHOW);
if (Minimized) ShowWindow(appWindow, SW_RESTORE);
Shell_NotifyIcon(NIM_DELETE, &NID);
}
break;}
Well, We have now defined the Callback procedure for our Notify Icon. Now, we will get the Window Messages for our Notify Icon. We can Handle those messages from our Message Handler itself.
ID_MENU_SHOW and ID_MENU_EXIT are two Window ID's i have given to the two menu Items. You may give your own.
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
if(wmId == ID_MENU_SHOW)
{
MessageBox(hWnd, "testing", "SysTrayIcon[My App]", MB_OK);
}
if(wmId == ID_MENU_EXIT)
{
PostQuitMessage(0);
}break;
Thats all... Now, you can run trhe code, and when right-clicked on the Tray Notification Icon, a Menu will popup with 3 options : Show, Show2 and Exit.
On clicking Show, it just pops up a Messagebox.. Clicking on Exit will terminate the application. You can customize the code for your need.
Sorry, if badly explained or anywhere i confused. I hope i've commented the code sufficiently and there is MSDN for your help in Documentation of API's.
I Strongly Recommend reading of MSDN for the API documentations. :)
PS : Please leave your valuable comments, and let me help improve :-)
PS : Please leave your valuable comments, and let me help improve :-)
Joaquim:
ReplyDeleteyou have errors with flags combinations, you don't use '|' symbol ;)
i didn't test the code , but it's greate ;)
maybe you can show us how add ballons too ;)
thanks for all
Yes it's very good and it works correctly with '|'.
ReplyDeleteCool thanks
Effixe(France):
ReplyDeleteThe best tutorial which I have found on the topic.
Now, I understand how to manage an icon in that particular area.
Thank You for your presentation