
Introduction
Welcome to my second article of Simplicity Is Virtue philosophy. Never heard of it? Of course, I created it.. (-: (If you're really interested what the heck is that, then take a look at my first article.) This article is aimed towards beginners, so all of you coding gurus and wizards can skip all of this. In this article, you can learn how to:
- create, modify and remove a tray icon
- define and use home-made window messages
Before we begin, let me explain why create YET ANOTHER tray icon class, when there are so many of them, free for use. Well, I was browsing the net, searching for a tray icon class, just being lazy of typing my own code. I actually found a whole bunch of them, free, powerful and quite often - loaded with features I don't need. That means - very irritating to use. So I said to myself, Today IS a good day to die, and started coding the most simple tray icon class anyone could ever think of :-). Let us begin.
Preparations
Before we do anything, let us recall: you create a tray icon by creating a NOTIFYICONDATA
structure, and giving it to the Shell_NotifyIcon
API function. That tends to be quite irritating, especially if you change the icon or the tooltip frequently during runtime. Because of that, we will create a new class, and provide ourselves a simple and clean interface.
First, we create a new dialog based project, all other options are irrelevant. We begin by adding a new generic class to our project. I named mine "CSimpleTray
". Now, we will take care of the private class variables: we need a single icon, a single tooltip, and a single NOTIFYICONDATA
structure. Besides that, we would need an indicator whether our icon is shown or not, so we will create a BOOL
variable called m_bEnabled
. The class should look like this by now:
class CSimpleTray
{
public:
CSimpleTray();
virtual ~CSimpleTray();
private:
BOOL m_bEnabled;
HICON m_hIcon;
NOTIFYICONDATA m_nid;
CString m_strTooltip;
};
Now let's see: besides showing and hiding the icon, we want to be able to change the icon and tooltip during runtime, and basically that's all we will do here. Simple interface will consist only of these functions:
void Show();
void Hide();
void SetIcon( HICON hIcon );
void SetTooltip( LPCTSTR lpTooltip );
Still, there is only one thing to think of: we want our icon to actually DO something when our user clicks on it. To implement that, we will make our icon send a notification to the app's main dialog, simply by sending a custom window message. First things first, we will define the message before our tray icon class declaration, like this:
#define WM_TRAYNOTIFY WM_APP+1
Now, we need to mess a bit with the dialog itself, add a new protected function declared like this:
afx_msg void OnTrayNotify( WPARAM wParam, LPARAM lParam );

Ok, so now that we have #define
d a window message and a function, we need to connect them. You do this by manually adding an entry to the framework's message map:
BEGIN_MESSAGE_MAP(CTrayDemoDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_MESSAGE(WM_TRAYNOTIFY, OnTrayNotify)
END_MESSAGE_MAP()
Note that there is no semicolon at the end of the line. Now, every time our dialog receives WM_TRAYNOTIFY
message, it will call the message we created - OnTrayNotify
. Of course, you will need to #include
the class declaration file, otherwise the compiler will complain that WM_TRAYNOTIFY
is not defined.
#include "stdafx.h"
#include "TrayDemo.h"
#include "TrayDemoDlg.h"
#include "SimpleTray.h" // <-- this one
Finally - the class..
Finally, we get to code the class. Let us check out the code:
CSimpleTray::CSimpleTray()
{
m_hIcon = NULL;
m_strTooltip = "";
m_bEnabled = FALSE;
}
CSimpleTray::~CSimpleTray()
{
Hide();
}
void CSimpleTray::Show()
{
if ( m_hIcon == NULL )
return;
if ( m_bEnabled == TRUE )
return;
m_nid.cbSize = sizeof(m_nid);
m_nid.hWnd = AfxGetMainWnd()->m_hWnd;
m_nid.uID = 100;
m_nid.uCallbackMessage = WM_TRAYNOTIFY;
strcpy(m_nid.szTip, m_strTooltip);
m_nid.hIcon = m_hIcon;
m_nid.uFlags = NIF_MESSAGE |
NIF_ICON |
NIF_TIP;
Shell_NotifyIcon( NIM_ADD, &m_nid );
m_bEnabled = TRUE;
}
void CSimpleTray::Hide()
{
if ( m_bEnabled == TRUE )
{
m_nid.uFlags = 0;
Shell_NotifyIcon( NIM_DELETE, &m_nid );
m_bEnabled = FALSE;
}
}
void CSimpleTray::SetIcon(HICON hIcon)
{
if ( hIcon == NULL )
return;
m_hIcon = hIcon;
if ( m_bEnabled == TRUE )
{
m_nid.hIcon = m_hIcon;
m_nid.uFlags = NIF_ICON;
Shell_NotifyIcon( NIM_MODIFY, &m_nid );
}
}
void CSimpleTray::SetTooltip(LPCTSTR lpTooltip)
{
m_strTooltip = lpTooltip;
if ( m_bEnabled == TRUE )
{
strcpy(m_nid.szTip, m_strTooltip);
m_nid.uFlags = NIF_TIP;
Shell_NotifyIcon( NIM_MODIFY, &m_nid );
}
}
That's it!
How to use it?
The most simple way would be to use your applications icon, and show the damn thing at the program startup, inside OnInitDialog()
. First, add a private variable of type CSimpleTray
to the dialog class:

and then use it like this:
m_pTrayIcon.SetIcon( m_hIcon );
m_pTrayIcon.SetTooltip( "Look at me!" );
m_pTrayIcon.Show();
All you need to do now, is set the icon's behavior, and you do that inside OnTrayNotify
. For example, we want to display a menu when a user right clicks on our icon. I created mine like this:

If you press CTRL+W to open the class wizard, you will get a dialog box with two options: to create a new class for your menu, or to select an existing one. Choose "Select an existing class" and press OK. On the next dialog, you can see a list of classes available in your project. The most appropriate class would be your main dialog class, in this case CTrayDemoDlg
. Select it and press OK. Now you can edit the code for your popup menu, and it will be inside CTrayDemoDlg
class.
Now, let us edit our callback function:
void CTrayDemoDlg::OnTrayNotify(WPARAM wParam, LPARAM lParam)
{
switch ( lParam )
{
case WM_LBUTTONDOWN:
{
OnShowdialog();
}
break;
case WM_RBUTTONUP:
{
CMenu temp;
CMenu *popup;
CPoint loc;
GetCursorPos( &loc );
temp.LoadMenu( IDR_MNUPOPUP );
popup = temp.GetSubMenu( 0 );
popup->TrackPopupMenu( TPM_LEFTALIGN, loc.x, loc.y, this );
}
break;
default:
break;
}
}
We also might want to hide the taskbar button of your dialog, and we can do it inside OnInitDialog()
. The demo app demonstrates how to accomplish that, and how to implement a simple "animation" - a flashing tray icon.
THE END - I hope someone will find this article useful in some way! (-:
Copyright
As for the copyright, all of this is copyrighted by me (T1TAN) and my pseudo-company SprdSoft Inc. That means that you can use this code in any app (private or commercial) without any restrictions whatsoever, since our company supports open source and free software. However, if you are going to use (parts of) the code, I'd like to hear from you, just to keep track of things.
If you have any comments, ideas or anything else about the article/code, feel free to contact me, I'd be glad to hear from you.
Greets & good programming!
History
- 25/01/2004 - First release.