Sometimes you are developing an ATL COM server that does not have a GUI, and you would
like an icon in the shell (or "system tray" as it's also known), to display some sort of
runtime information. I needed this for a project I was working on at the time, and was
inspired by the little icon that appears when you connect to the Internet with your modem.
It shows you how long you've been connected, and the number of bytes sent and received.
The icon also changes when there is activity - a nice touch.
I had a look at some of the source code available on Code Project, but none did exactly
what I wanted. For a start most of the ATL servers I was developing did not have a GUI
or a window associated with them. I wanted a utility class that I could derive from when
creating my ATL COM server that worked out of the box, and that handled all the complexities
of creating a hidden window, dealing with menus etc.
So what does this code do? Well this class allows you to display an icon of your choosing
in the shell, which can be changed as often as you like. You can update the tip text
associated with the icon when you like. You can respond to mouse events that are generated
over your tray icon. And, the class will display a popup menu of your choosing on demand.
You might want to check out other articles on Code Project concerning the shell. This
is just a very simple class to get support for the system tray into your ATL COM server
quickly. If nothing else then it's example code. This code was developed with Visual Studio
6.0, I haven't tested it in Visual Studio .NET.
Compile the Visual C++ project and make sure it registers the COM server that it builds.
This has a minimal COM server with some functions to demonstrate how to use the helper class.
Start the VB demo application ShellTest.exe; you will see an icon appear in the shell area.
You can enable or disable the icon by clicking on the "Visible in shell" checkbox. Type some text
into the tip edit box and click "Update", and the tip text will change.
Check the "animate" button and the icon will change. You can alter how often the icon
animates by entering a valid (say, 100) in the frequency edit box, and then click the
Right click on the shell icon. You will see a context menu appear. Select "About" and a dialog
Using the CShellIconHelper class
The class has a number of functions that you'll want to call to work with the shell:
virtual void SetShellTipText (std::string &TipText)
SetShellTipText to pass in the tip text you want displayed. If you specify more than
64 characters, it will be truncated. This is because the basic version of the common controls only
supports this number of characters.
virtual void SetShellIcon (WORD IconResource)
Pass in the resource ID of the icon (e.g.
IDI_ICON1) that you want displayed in the shell.
virtual void SetShellTimer (bool bEnabled, WORD wTimerDuration)
SetShellTimer to enable a windows timer for your shell icon, if you want to use one. Your
ATL server class will then receive
WM_TIMER messages, which you can do some processing with.
You can also switch off a running timer by calling this with
bEnabled = false. The timer duration is specified
virtual void SetShellVisible (bool bVisible = true)
true displays the icon in the shell, and if you have requested
to use the timer, starts it off. Calling this with
false removes the icon from the shell
and stops any timer.
virtual WORD ShowPopupMenu (WORD PopupMenuResource)
ShowPopupMenu with the resource ID of your menu (e.g.
IDR_POPUP_MENU). It will
return back the menu ID that was selected, or zero if no menu item was selected.
How to use this code in your project.
I'm assuming you used the wizard to create an ATL COM server, and have added an ATL object to your project.
All the changes you'll need to make are to the header file of your ATL object.
Add the file ShellIconHelper.h to your workspace. This contains the class
In the header file for your COM server, add in the line
You might also need to change project settings for your COM server so that exceptions are
supported, as the helper class uses STL. Go to project/settings and select "C++ language"
category. Check the "Enable exception handling". If you really don't want exception handling
then you could just change the
ShellIconHelper class to use
LPCSTR instead of
quite a trivial change.
Add the class
CShellIconHelper to the list of derived classes of your COM server class.
As it's a templated class you need to specify the name of the class in angle brackets,
in the same fashion as many of the other derived classes listed.
class ATL_NO_VTABLE CMyServer :
public CComCoClass<CMyServer, &CLSID_MyServer>,
public IDispatchImpl<IMyServer, &IID_IMyServer,
Add a message map to this class (in the section with the other macro declarations),
so that your COM server handles windows messages correctly. In this case we want to respond
to timer messages and user commands (i.e. when the user moves the mouse over our shell icon).
If you don't want to use the timer or user commands, just leave out the
But you must have the
in your class - otherwise it won't compile.
Override the method
OnFinalConstruct in this class. We'll set up the icon in the system
tray here. Note, you can choose to display the icon at any time once your server is loaded
(or not at all). I chose
OnFinalConstruct as it's a handy place to do this.
SetShellTipText (std::string("Some tip text"));
SetShellTimer (true, 1000);
Override the method
OnFinalRelease in this class. The code we add here removes the
icon from the system tray when the application shuts down. Note: You must include this
section of code, with the call to
SetShellVisible (false) otherwise your
application may well get an access violation during unloading.
void FinalRelease ()
Add a handler in for the timer messages. This gets called periodically, at the
frequency you specified in the call to
SetShellTimer. If you want to change
the icon or the text for your system tray icon, you should call
LRESULT OnTimer(UINT uMsg, WPARAM wParam,
LPARAM lParam, BOOL& bHandled)
Add a handler in for user commands. This allows you to respond to mouse
events over your system tray icon. For example, you'll probably want to display a
popup menu when the user right clicks your icon, as shown here. The
lParam contains the message
that Windows has passed you. You call the function
ShowPopupMenu, with a Resource
ID of a menu you want displayed. This function returns the ID of the menu option
selected (as defined in the resource files for your project), or 0 if no menu option
LRESULT OnUserCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL&bHandled)
if (lParam == WM_RBUTTONUP)
WORD cmd = ShowPopupMenu (IDR_POPUP_MENU);
Detailed explanation of code
template <typename T>
class CShellIconHelper : public CWindowImpl<T>
CShellIconHelper derives from the ATL class
CWindowImpl - this gives us our hidden window,
that we need to use to display the icon and to receive windows messages. If your ATL object
already derives from
CWindowImpl (maybe it's an ActiveX control), then you can remove this
CWindowImpl gives us a public member
m_hWnd - this stores the window handle of our
void ShellNotify (DWORD msg)
m_CurrentText = m_CurrentText;
m_CurrentIconResource = m_CurrentIconResource;
notifyIconData.cbSize = sizeof(notifyIconData);
notifyIconData.hWnd = m_hWnd;
notifyIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
notifyIconData.uCallbackMessage = WM_USER;
notifyIconData.uID = 0;
notifyIconData.hIcon = ::LoadIcon(_Module.GetResourceInstance),
::lstrcpyn(notifyIconData.szTip, m_CurrentText.c_str(), 64);
::Shell_NotifyIcon (msg, ¬ifyIconData);
ShellNotify does the actual work of updating the shell icon and tip text. It's really quite
simple - just fill in the
NOTIFYICONDATA structure with the required information and call
Shell_NotifyIcon. Note that the shell supports "balloon messages" on certain
versions of Windows with the newer version of the common controls.
I decided against supporting these, as I wanted the code to run on as many platforms
virtual WORD ShowPopupMenu (WORD PopupMenuResource)
HMENU hMenu, hPopup = 0;
hMenu = ::LoadMenu (_Module.GetModuleInstance(),
if (hMenu != 0)
hPopup = ::GetSubMenu (hMenu, 0);
WORD cmd = ::TrackPopupMenu (hPopup, TPM_RIGHTBUTTON | TPM_RETURNCMD,
pt.x, pt.y, 0, m_hWnd, NULL);
::PostMessage (m_hWnd, WM_NULL, 0, 0);
The only other noteworthy piece of code is the function
ShowPopupMenu, which displays and then
tracks a popup menu. Note that it gets a "sub menu" from the menu you have defined,
as you cannot display a menu bar. Also it calls
SetForegroundWindow before it tracks the
pop up menu; this is required so that if you click away from the popup menu without
selecting an item it will automatically disappear. You must also post a message to the window after tracking the popup menu;
this behavouir is "by design"! The function returns the menu item
you selected, or zero if you didn't select anything.
I tested this project with an ATL DLL and ATL EXE server. I didn't try with a service - there are
other articles on Code Project you might want to look at for services. I also want to get this to work
under Visual Studio .NET. Finally the class should support "Balloon" tips.