When I started working on my current project, I noticed that lots of programs I was working on, required notification icons (yeah, tray icons) because of some reasons. Actually, writing a couple of calls to
Shell_NotifyIcon() is not a big deal, but this project required animated tray icon. And I decided that I have something to do with all these tray icons. The result of my work is here. Meet —
If you want to create a similar class, you'll need to read about the famous
Shell_NotifyIcon() function and take a look at the IconPro MSDN sample (see "Icons in Win32" article by John Hornick from Sep 29, 1995).
Shell_NotifyIcon() will give you an ability to control notification icon and the sample will show you how to extract planes from the icon resource.
If you do not know yet, the icons in Windows can have multiple so-called planes. The plane is actually an image. Usually icons have 16x16x16, 32x32x256 and 48x48x256 planes, so Windows Explorer can display the icons correctly in different video and list modes. But the icons can have saying, 10 16x16x16 planes or 25 32x32x16M planes — whatever you want. I decided to use such kind of the icons to make animated notification icons.
Don't worry — almost all good icon editors can make such icons. Not the editor of Visual Studio, though.
Using the Code
CakTrayIcon is easy. Just declare the object of this class.
CakTrayIcon ti(this, 1, LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME)),
Your icon should be visible all the time? No problem.
First, edit your header file.
class CMainFrm : public CFrameWnd
Then, initialize the icon in
int CMainFrm::OnCreate(LPCREATESTRUCT lpCreateStruct)
if (CWnd::OnCreate(lpCreateStruct) == -1)
m_TrayIcon.Create(this, 1); m_TrayIcon.SetIconAndTip(LoadIcon(AfxGetInstanceHandle(),
_T("This is a tray icon"));
That's all. The icon will be destroyed when the window will be closed.
No problem to handle the events from your icon too.
Declare the message handler in the header.
class CMainFrm : public CFrameWnd
afx_msg LRESULT OnTrayIconNotify(WPARAM, LPARAM);
And add it to the message map.
LRESULT CMainFrm::OnTrayIconNotify(WPARAM, LPARAM)
Please, refer to
NOTIFYICONDATA.uCallbackMessage for details about the message you'll receive. Note that you'll receive the id of the icon in
wParam parameter. Also note that you should use
ON_REGISTERED_MESSAGE macro along with
CakTrayIcon::WM_TRAYICONNOTIFY parameter to add the notification message into the map.
Implementing "just another tray icon class" is boring. So I added a couple of helper functions.
You can change an icon, a tip or both using the functions below.
m_TrayIcon.SetIconAndTip(LoadIcon(0, IDI_ERROR), _T("Hi!"));
And, of course, you can show an information balloon.
m_TrayIcon.PopupBalloon(_T("Some information"), _T("Balloon title"),
Note that balloon tip can be used only if you targeted IE version 5.0 or later (see
_WIN32_IE macro). In other cases you will be able to call this method, but nothing will happen.
Wake Up! The Most Interesting Part!
Before using the following code, you'll need at least one multi-plane icon. Please, take a look at hourglass.ico in folder res of the sample project for example.
Assume you want to start lenghtly operation. You'd like to animate something in the system notification area to make the process more interesting for user. Use
CakTrayIconAnimator for this reason.
And again, this is simple as 1-2-3.
CakTrayIconAnimator tia(&m_TrayIcon, IDI_HOURGLASS, _T(
"Hey! Don't get sleep!"));
_T("Click OK when you tired to look at the hourglass spinning around..."),
Note that you have to have a valid object of
CakTrayIcon class and an integer code or string name of the multi-plane icon resource.
CakTrayIconAnimator will save the current tray icon and tip and restore them after animation completed.
That's it. You don't have to write tons of code to show and animate your icons. Animation is implemented using thread, so you don't have to write any support code for it.
Points of Interest
When I was working on
CakTrayIconAnimator class, I found that extract a plane from the icon is a pretty tricky thing. Thanks God, I found IconPro sample by John Hornick in MSDN and added several functions from there to
CakIconLoader class. I made this class public, so you can use it if you need to extract icon planes too.
UINT count = ldr.GetIconsCount(); HICON hIcon = ldr.GetIcon(0); }
GetIcon() — this icon handle will be freed by next
GetIcon() call or when object of
CakIconLoader will be destroyed.
Interesting fact — when working on
CakIconLoader, I found that I should use
CreateIconFromResourceEx() with zeroed flags instead of
CreateIconFromResource() creates shared icon and this mean that you'll have resource leak when call it constantly. This difference cost me lots of nerves and time.
Burn in your mind that all Windows except XP, 2003 and Me can display tray icons only in 16 colors (despite what MSDN says). So, if you want your icons to look good, make them 16x16x16 if you target any Windows except XP, 2003 and Me, and make hi-color version for XP, 2003 and Me.