Click here to Skip to main content
Click here to Skip to main content

Easy Animated Tray Icon

By , 4 Feb 2002
 

Sample Image

Introduction

This is a class (SS_TrayIcon) that creates a tray icon in the system tray, allowing easy integration into any project, whether or not you use MFC. Some of the features include:
  • Create multiple icons, animated icons, blinking icons, or still icons
  • Easily define a message map for routing Windows messages generated by the tray icon to user defined functions...
    -or-
    Let MFC handle the message mapping by sending all messages to the CWnd::WindowProc function.
  • Easily add a popup window (context menu) to the icon when the user clicks on it.
  • Avoid processing the WM_LBUTTONDOWN Windows message before a user double-clicks. This is a common problem... when the user double-clicks, programs often process a single-click before processing the double-click. This class will wait to send the single-click message until it is sure that the user won't double-click. (note: this feature does not work if you let MFC handle the message routing.)

This class is similar to Chris Maunder's class in Adding Icons to the System Tray except that it allows you to specify the mode (SSTI_MODE_SHOWNORMAL, SSTI_MODE_HIDE, SSTI_MODE_BLINK, SSTI_MODE_ANIMATE)

A simple demo project is included:

Sample Image

Integration Into Your Project

You will need to include the 2 header files into your project (SS_TrayIcon.h and SS_Wnd.h), add the SS_TrayIconD.lib file to your debug project, and the SS_TrayIcon.lib file to your release project. (or you can skip the lib files and add the two *.cpp files instead.)

To create a tray icon, you only need 3 lines of code:
SS_TrayIcon* m_pTrayIcon = new SS_TrayIcon( m_hInstance, 1, 1 );  // create the instance
m_pTrayIcon->LoadIcon( 0, 0, IDI_SOME_ICON_RESOURCE );            // load an icon resource
m_pTrayIcon->ShowIcon( 0 );                                       // show it!
Some other options include:
m_pTrayIcon->Mode( 0, SSTI_MODE_BLINK );                      // make it blink
m_pTrayIcon->SetAnimateSpeed( 0, 400 );                       // 400 is milliseconds
m_pTrayIcon->ToolTip( 0, _T("This is a blinking icon...") );  // add a tool tip
m_pTrayIcon->ShowMenuAtMouse( nMenuResourceID, hWnd );        // show a popup menu

m_pTrayIcon->HideIcon();                                      // hide it!
Most of the functions that alter the icon require an integer as the first parameter (nIconSet). Because you can have multiple icons, this integer specifies which icon you want to alter. Then when you show the icon, you specify which icon you want to show. From here on out, we will refer to each one of these icons as an IconSet... here's why: each IconSet can contain multiple "sub-icons", which we will refer to as "frames". So each IconSet makes up an animation that is composed of some number of frames. You can load icon resources into the frames of your IconSets as in the following code, which creates 3 IconSets, one with 8 frames, and two with only 1 frame:
                                        //             How many icons (IconSets)?
                                        //             |  How many icons per set (frames)?
                                        //             |  |
m_pTrayIcon = new SS_TrayIcon( AfxGetInstanceHandle(), 3, 8 );


// prep IconSet 1 (animated with 8 frames)
//
//                     This is which IconSet to load into...
//                     |  This is the frame number...
//                     |  |
m_pTrayIcon->LoadIcon( 0, 0, IDI_ICON1 );
m_pTrayIcon->LoadIcon( 0, 1, IDI_ICON2 );
m_pTrayIcon->LoadIcon( 0, 2, IDI_ICON3 );
m_pTrayIcon->LoadIcon( 0, 3, IDI_ICON4 );
m_pTrayIcon->LoadIcon( 0, 4, IDI_ICON5 );
m_pTrayIcon->LoadIcon( 0, 5, IDI_ICON4 );
m_pTrayIcon->LoadIcon( 0, 6, IDI_ICON3 );
m_pTrayIcon->LoadIcon( 0, 7, IDI_ICON2 );

m_pTrayIcon->Mode( 0, SSTI_MODE_ANIMATE );
m_pTrayIcon->SetAnimateSpeed( 0, 150 );
m_pTrayIcon->ToolTip( 0, _T("This is an animated icon...") );


// prep IconSet 2 (blinking)
//
m_pTrayIcon->LoadIcon( 1, 0, IDR_MAINFRAME );

m_pTrayIcon->Mode( 1, SSTI_MODE_BLINK );
m_pTrayIcon->SetAnimateSpeed( 0, 450 );
m_pTrayIcon->ToolTip( 1, _T("This is a blinking icon...") );


// prep IconSet 3 (standing still)
//
m_pTrayIcon->LoadIcon( 2, 0, IDI_ICON6 );

m_pTrayIcon->Mode( 2, SSTI_MODE_SHOWNORMAL );
m_pTrayIcon->ToolTip( 2, _T("This is a non-animated icon...") );


// Now show the first (animated) icon
//
m_pTrayIcon->ShowIcon( 0 ); // or '1' for the second, or '2' for the third

The last thing you need to know is about the message map. For each message you want to respond to, you need the call the m_pTrayIcon->MapMessageToFunction function. You need to supply the function with a pointer to your global callback function so it can call that function when the message is generated, as so:
// *global* callback function for the WM_LBUTTONDBLCLK message
LRESULT CALLBACK OnMouseDblClickTI(WPARAM wParam, LPARAM lParam)
{
    ::MessageBox(NULL, _T("Double-clicked!!"), _T("Test"), MB_OK|MB_ICONINFORMATION);
    return 0;
}

// in your initialization routine
CYourClass::InitObject()
{
	...

	m_pTrayIcon = new SS_TrayIcon( AfxGetInstanceHandle(), 3, 8 );

	m_pTrayIcon->MapMessageToFunction(WM_LBUTTONDBLCLK, ::OnMouseDblClickTI);

	...
}
Now whenever the user double-clicks on the tray icon (no matter which one you have showing), your OnMouseDblClickTI() function will be called. You can do anything you want from there. If you want to respond differently depending on which icon you have showing, you will have to keep track of which icon is showing and use a switch in the callback function to determine what action is appropriate.

Notes

There are LOTS of comments in the demo code explaining how to use the SS_TrayIcon class, and there are also lots of comments in the SS_TrayIcon code explaining how the code works, so please read through those comments for a much more detailed explanation of the inner workings of this project.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Steve Schaneville
Architect
United States United States
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralRe: NIF_INFO flagmemberSteve Schaneville5 Oct '04 - 16:36 
sorry, I've not touched this code in over 2 years. So no, I don't plan on making any more changes. Take it as an exersize if you're interested... shouldn't be difficult Wink | ;-)
 
~steve
GeneralGreat.memberstotti_no14 Oct '04 - 3:25 
Thanks for the code example. Its just great! It helped me a lot to easily write an app with an animated tray icon. Cool | :cool:
GeneralRe: Great.memberSteve Schaneville4 Oct '04 - 16:40 
Thanks! Glad it worked for you.
 
~Steve
GeneralBug when using IE 5.0+memberintensely_radioactive7 Jun '04 - 16:37 
Hi Steve... Great sample, thanks for sharing.
 
I've been using SS_TrayIcon for a while without problems on Win2K VC6, then I updated to Microsoft Platform SDK Feb 2003. Then the tooltips for the tray icons were all gibberish.
 
Stepping through the code, I noticed that the Platform SDK had enabled this piece of code:
 
    // do the deed
    NOTIFYICONDATA nif;
 
    nif.cbSize = sizeof(NOTIFYICONDATA);
    nif.hWnd = WindowHandle();
    nif.hIcon = IconSet(nIconSet).pIcon[IconSet(nIconSet).nCurrentIcon].hIcon;
    nif.uID = 1;
#if(_WIN32_IE>=0x0500)
        nif.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO;
#else
        nif.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
#endif
 
Note that the added NIF_INFO flag has validated several members of the NOTIFYICONDATA struct that are uninitialized. This happens in 2 (or 3) places in the code. To fix it, I added:
 
    // do the deed
    NOTIFYICONDATA nif; 
	memset( &nif, 0, sizeof(NOTIFYICONDATA) );
    nif.cbSize = sizeof(NOTIFYICONDATA);
    nif.hWnd = WindowHandle();
    nif.hIcon = IconSet(nIconSet).pIcon[IconSet(nIconSet).nCurrentIcon].hIcon;
    nif.uID = 1;
#if(_WIN32_IE>=0x0500)
        nif.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO;
	nif.uVersion = NOTIFYICON_VERSION;
	nif.dwInfoFlags = NIIF_NONE;
	_tcscpy(nif.szInfo, IconSet(nIconSet).szToolTip);
	_tcscpy(nif.szInfoTitle, _T("Title"));
#else
        nif.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
#endif
 
Thanks again for sharing!
 
tom.
GeneralRe: Bug when using IE 5.0+memberSteve Schaneville8 Jun '04 - 12:10 
Excellent, Thanks! I'll add that to my code...
 
~Steve
GeneralLoadIcon vs LoadImagemembereugeneugene@yahoo.com16 May '04 - 21:37 
Hello!
 
Nice Animated Icon, thank you! But I suppose that you should use LoadImage instead LoadIcon for its flexible.
LoadIcon tries to load 32x32 icon and shrinks it to 16x16, while LoadImage lets you select desired size.
 
Eugene
GeneralRe: LoadIcon vs LoadImagememberSteve Schaneville17 May '04 - 2:19 
That would be great if you could put an icon any size other than 16x16 into the task tray, but you can't, no?
 
~Steve
GeneralRe: LoadIcon vs LoadImagemember_gl4 Aug '11 - 11:52 
Actually when the OS GUI is set to a high DPI, 32x32 icons may be used.
 
The other problem with LoadIcon() is that if you have an icon resource with both 16x16 and 32x32 icons, it may load the 32 version and squash it down to 16 (looks terrible). LoadImage allows control of the size used (though I'm not yet sure what the right params are for both those scenarios).
GeneralShow/HideMinimize Window Statemembertyounsi18 Feb '04 - 4:31 
Your application is really cool but to be complete you should have catch the WM_MINIMIZE message in order to hide a dialog in the tray icon.
 
Best
 
--
Thomas
 
Programming is not an end in itself but only a means to an end
GeneralRe: Show/HideMinimize Window StatememberSteve Schaneville18 Feb '04 - 16:51 
Hmmm... good idea. I've not messed with this class in a long time, but if I do again I'll add that feature. Thanks for the suggestion.
 
~Steve

GeneralExcellent examplememberGifProg16 Nov '03 - 9:33 
thanks is a very nice example, and teach me a lotOMG | :OMG:
GeneralRe: Excellent examplememberSteve Schaneville18 Nov '03 - 10:46 
Thanks! Glad you got some use out of it Wink | ;)
GeneralCompiling in .NETmemberWolfSupernova28 Oct '03 - 5:23 
Just a note...
 
It looks like I'm not the only one who's had trouble incorporating this into their projects, though it's a great library to use if it's ABSOLUTELY NECESSARY to have a System Tray icon. I only say that because a lot of developers use them in their projects because they think they're cool, but if they serve no logistical purposes, they're a waste of space and can be annoying to end-users. That of course, is just my opinion.
 
What seems to be the best way (and please someone correct me if I'm wrong) to include this into a VC7 MFC application, is to add the "SS_TrayIcon.h", "SS_Wnd.h", and "SS_TrayIcon.lib" files into your project, and rather than including "SS_TrayIcon.lib" into the project settings, add "LIBC.lib" to the "Ignore Specific Library" field in the settings, add #include "SS_TrayIcon.h" to your header file, and continue as directed. That should take care of it.
 
There's some KB articles and more info on MSDN about when/why to ignore specific libraries.
 
Hope that helps!
GeneralRe: Compiling in .NETmemberSteve Schaneville28 Oct '03 - 6:16 
Wolf, thanks for your input. I've never tried it in VC7 or .NET, so I have no idea what problems you might run into. It was developed in VC6.0. Anyway, thanks for your digging into that problem and posting the solution Wink | ;)
 
~Steve
 

GeneralRe: Compiling in .NETmemberKhumpty7 Nov '03 - 4:59 
I was having the same problem but trying this solution didn't work for me. Including the .cpp files made things even worse.
I suppose that adding "LIBC.lib" to the "Ignore Specific Library" field in the settings is supposed to cure this problem but i still get things like this when linking:
 
warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/INCREMENTAL:NO' specification
error LNK2005: __amsg_exit already defined in msvcrtd.lib(MSVCR70D.dll)
error LNK2005: _exit already defined in msvcrtd.lib(MSVCR70D.dll)
error LNK2005: __exit already defined in msvcrtd.lib(MSVCR70D.dll)
error LNK2005: __cexit already defined in msvcrtd.lib(MSVCR70D.dll)
.
.
.
 
Any help would be appreciated.
 
TIA
 

GeneralRe: Compiling in .NETmemberKhumpty7 Nov '03 - 5:28 
NM, I got it.
GeneralErrors...memberSelevercin6 Apr '02 - 9:11 
Hi,
I'm not sure why, but I got the following errors when I downloaded your source code.
 

S_TrayIcon.obj : error LNK2001: unresolved external symbol "public: virtual unsigned int __thiscall SS_Wnd::SetTimer(unsigned int,unsigned int,void (__stdcall*)(struct HWND__ *,unsigned int,unsigned int,unsigned long))" (?SetTimer@SS_Wnd@@UAEIIIP6G
XPAUHWND__@@IIK@Z@Z)
LIBCD.lib(crt0.obj) : error LNK2001: unresolved external symbol _main
Debug/SS_TrayIcon.exe : fatal error LNK1120: 2 unresolved externals
Error executing link.exe.
 
SS_TrayIcon.exe - 3 error(s), 0 warning(s)
 
I included the 2 *.h files and I included the 2 *.lib files... whats the matter?
 
Thanks,
 
~Selevercin
GeneralRe: Errors...memberschaneville18 Apr '02 - 17:45 
When you say you "included the two *.lib files", you don't mean you #included them, do you? Add them to the "Object / Libraries" edit box under the "links" tab in the project settings. If you still get the problem after that, send me your project and I'll figure it out for you...
Generalme again ...memberAnonymous8 Feb '02 - 16:31 
Can you show me the way to add event response on icon with MFC?
For example WM_RBUTTONCLIK and so on ...
GeneralRe: me again ...memberschaneville11 Feb '02 - 5:26 
When you're using MFC, you have 2 choices:
 
1. let the SS_TrayIcon class handle the message map, or
2. let MFC handle the message map.
 
both methods are detailed below:
 

Method 1: SS_TrayIcon handles mapping
-------------------------------------
 
a) When you create your SS_TrayIcon, supply NULL for the 4th parameter or just leave it off.
b) After that, call the SS_TrayIcon mapping function:
 
m_pTrayIcon = new SS_TrayIcon( AfxGetInstanceHandle(), 3, 8, NULL);
m_pTrayIcon->MapMessageToFunction( WM_RBUTTONUP, ::OnMouseRightClickTI );
 
c) now create a *global* message-handling function for that message (the function's name was given as the 2nd parameter in the MapMessageToFunction call above.
 
LRESULT CALLBACK OnMouseRightClickTI(WPARAM wParam, LPARAM lParam)
{
    ::MessageBox(NULL, _T("Right-clicked!!"), _T("Test"), MB_OK|MB_ICONINFORMATION);
    return 0;
}
 
That's all!
 

Method 2: MFC handles mapping
-----------------------------
 
a) When you create your SS_TrayIcon, for the 4th parameter supply the HWND of your application window that will handle the messaging:
 
BOOL MyMFCAppDlg::OnInitDialog()
{
    ...
    m_pTrayIcon = new SS_TrayIcon( AfxGetInstanceHandle(), 3, 8, m_hWnd);
    ...
}
 
b) now with the ClassWizard, create the WindowProc function, and add a switch that checks for the SSTI_CALLBACK_MESSAGE message. Inside that switch, create another switch that looks for the messages that you want to handle, such as your WM_RBUTTONUP message:
 
LRESULT CTestSSTrayIconDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
{
    // add the switch and look for the SSTI_CALLBACK_MESSAGE message, as
    // these messages were generated by the tray icon...
    //
    switch( message )
    {
    case SSTI_CALLBACK_MESSAGE:
        {
            // all messages here were generated by the tray icon, so 
            // process whichever message types you'd like.
            //
            switch( (UINT)lParam )
            {
            case WM_LBUTTONDOWN:
                // do whatever you want here
                m_pTrayIcon->ShowMenuAtMouse(IDR_MENU1, m_hWnd);
                break;
 
            case WM_RBUTTONDOWN:
                // do whatever you want here
                break;
 
            case WM_LBUTTONDBLCLK:
                // do whatever you want here
                break;
            }
        }
        break;
 
    default:
        break;
    }    
	
    return CDialog::WindowProc(message, wParam, lParam);
}
 
Have fun!!
 

GeneralThank you!memberKaravaev Denis5 Mar '02 - 23:00 
Much!
QuestionError???memberKaravaev Denis6 Feb '02 - 23:12 
error LNK2001: unresolved external symbol "public: int __thiscall SS_TrayIcon::ShowIcon(int,enum SsTiMode)" (?ShowIcon@SS_TrayIcon@@QAEHHW4SsTiMode@@@Z)
 
What I did wrong??

 
==============
www.design.kg
 

AnswerRe: Error???memberschaneville7 Feb '02 - 10:30 
The only thing I can think of is that you created your own project and included my header files, but did not include the library files or did not insert the *.cpp files into your project.
 
I downloaded the demo project from codeguru to check them and they work fine for me. Just make sure that your are including the library files in your projects (or compiling the cpp files with your project). If that doesn't work, email me again... Wink | ;)
 
~ Steve
GeneralRe: Error???memberKaravaev Denis7 Feb '02 - 18:35 
Yah!
I'm trying to use this thing in my own proj. and I'm included all needet files ty my proj.
 
I have downloaded sample from codeguru and it work correctly too. Thats why I can't understand why it doesn't work ...
 
==============
www.design.kg
 

GeneralRe: Error???memberschaneville8 Feb '02 - 4:53 
Hmmm... strange. Are you getting the same linking errors that you were getting in your previous message?
 
Would you be willing to send me your project (is it small enough and will your company let you send it)? I'd be happy to take a look as to why these errors are coming up.
 
Are you using VC++ 6.0 sp4? I am, and I've not tested this code on any other compiler, but I can't really imagine why it wouldn't work on older compiler versions.
 
Send me the exact error message (all that show up).

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 5 Feb 2002
Article Copyright 2002 by Steve Schaneville
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid