Click here to Skip to main content
12,889,437 members (52,656 online)
Click here to Skip to main content


57 bookmarked
Posted 20 Jun 2002

Placing an icon in the system tray from an ATL COM server - with minimum hassle

, 23 Jun 2002
This article describes a helper class that assists with placing an icon in the shell (aka "system tray"), and changing the tip text. You can get this functionality by simply deriving your ATL object from the helper class.
// Shell Icon Helper - by Jon Taylor, for Code Project
// See documentation on Code Project web site for information on how 
// to use this class.


#include <string>
#include <atlwin.h>

template <typename T>
class CShellIconHelper : public CWindowImpl<T>

    virtual HRESULT CreateShell ()
        RECT rect;
        rect.left = rect.right = = rect.bottom = 0;

        // Create a hidden window (using CWindowImpl)
        HWND hWnd = Create (NULL, rect, "ShellIconHiddenWindow", WS_POPUP);

        if (hWnd != 0) // Was created?
            // Add the icon into the shell
            ShellNotify (NIM_ADD);
            return S_OK;
        else return HRESULT_FROM_WIN32 (GetLastError());
    virtual void DestroyShell ()
        ShellNotify (NIM_DELETE); // Remove the icon
        if (m_hWnd != NULL)
            // Get rid of the hidden window

    void ShellNotify (DWORD msg)
        m_CurrentText = m_CurrentText;
        m_CurrentIconResource = m_CurrentIconResource;
        NOTIFYICONDATA notifyIconData;
        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(), MAKEINTRESOURCE (m_CurrentIconResource));
        ::lstrcpyn(notifyIconData.szTip, m_CurrentText.c_str(), 64); // Limit to 64 chars
        ::Shell_NotifyIcon (msg, &notifyIconData);    

    CShellIconHelper () 
        // Initialise internal variables to known good state
        m_bVisible = false;
        m_bTimerActive = false;
        m_TimerId = 0;
        m_wTimerDuration = 0;
        m_CurrentIconResource = 0;
        m_CurrentText = std::string("");
    virtual void SetShellTipText (std::string &TipText)
        // Save this text for when we update
        m_CurrentText = TipText;
        ShellNotify (NIM_MODIFY);

    virtual void SetShellIcon (WORD IconResource)
        // Save this icon resource for when we update
        m_CurrentIconResource = IconResource;
        ShellNotify (NIM_MODIFY);

    virtual void SetShellTimer (bool bEnabled, WORD wTimerDuration = 1000)
        if (bEnabled == true) // User wants to start a timer
            if (m_bTimerActive = true)
                ::KillTimer (m_hWnd, m_TimerId);
            // Start the timer
            m_TimerId = ::SetTimer (m_hWnd, 1, wTimerDuration, NULL);
        else // User wants to shut down the timer
            if (m_bTimerActive = true)
                ::KillTimer (m_hWnd, m_TimerId);
        m_wTimerDuration = wTimerDuration;
        m_bTimerActive = bEnabled;

    virtual void SetShellVisible (bool bVisible = true)
        if (bVisible == true) // User wants to show the icon in the shell
            if (m_bVisible == false) // Doesn't already exist?
                // Create the shell, and timer (if applicable)
                CreateShell ();
            } // Otherwise, well you already have icon in the shell. :-)

            SetShellTimer (m_bTimerActive, m_wTimerDuration);

        else // User wants rid of the icon
            if (m_bVisible == true)  // Is it there already?
                // Destroy any running timer
                if (m_bTimerActive == true)
                    ::KillTimer (m_hWnd, m_TimerId);
                DestroyShell (); // Get rid

        m_bVisible = bVisible;

    virtual WORD ShowPopupMenu (WORD PopupMenuResource)
        HMENU hMenu, hPopup = 0;

        hMenu = ::LoadMenu (_Module.GetModuleInstance(), MAKEINTRESOURCE (PopupMenuResource));

        if (hMenu != 0)
            POINT pt;
            ::GetCursorPos (&pt);

            // TrackPopupMenu cannot display the menu bar so get 
            // a handle to the first shortcut menu. 
            hPopup = ::GetSubMenu (hMenu, 0);

            // To display a context menu for a notification icon, the 
            // current window must be the foreground window before the 
            // application calls TrackPopupMenu or TrackPopupMenuEx. Otherwise, 
            // the menu will not disappear when the user clicks outside of the menu 
            // or the window that created the menu (if it is visible). 
            ::SetForegroundWindow (m_hWnd);

            WORD cmd = ::TrackPopupMenu (hPopup, TPM_RIGHTBUTTON | TPM_RETURNCMD, pt.x, pt.y, 0, m_hWnd, NULL);
            // See MS KB article Q135788
            ::PostMessage (m_hWnd, WM_NULL, 0, 0);

            // Clear up the menu, we're not longer using it.
            ::DestroyMenu (hMenu);
            return cmd;     
		return 0;


    bool m_bVisible;
    bool m_bTimerActive;
    UINT_PTR m_TimerId;
    WORD    m_wTimerDuration;

    int m_CurrentIconResource;
    std::string m_CurrentText;


By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.


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

Jon Taylor
Web Developer
United Kingdom United Kingdom
I first started tinkering with a ZX81 back in 1981 and it's lovely blocky graphics, teaching myself Z80 assembler and BASIC. I come from a hardware background and electronics - I started out in embedded software for avionics but soon moved onto PC platforms.

Java and C++ are my prefered language but also dabble in VB and .NET.

I'm a team leader with Nokia developing Series 40 features - it's a cool place to work!

You may also be interested in...

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.170424.1 | Last Updated 24 Jun 2002
Article Copyright 2002 by Jon Taylor
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid