Adding Icons to the System Tray

原创 2006年06月23日 15:34:00
Win9x samplewinCE samplewin2000 sample
Example images in Windows 9x, Windows CE and Windows 2000

Minimising an application to the system tray
Default message handling
Example of use
NOTE on TrackPopupMenu


CSystemTray is a conglomeration of ideas from the MSJ "Webster" application, sniffing round the online docs, from other implementations such as PJ Naughter's "CTrayNotifyIcon" (, and from the many contributions from other developers.

This class is a light wrapper around the windows system tray stuff. It adds an icon to the system tray with the specified ToolTip text and callback notification value, which is sent back to the Parent window.

The Old way:

The basic steps to using a tray icon via the windows API are:

  1. Load up the NOTIFYICONDATA structure
  2. Call Shell_NotifyIcon(NIM_ADD, &MyTrayNotifyStruct)

Changing the values of the fields in NOTIFYICONDATA and calling Shell_NotifyIcon allows you to change to icon or tool tip text or remove the icon itelf. All this messing around has been bundled in a class wrapper to make it easier and neater.

The Better way

The simpler way to add an icon to the system tray is to create an object of type CSystemTray either as a member variable or dynamically. Two forms of the constructor allow the programmer to insert the icon into the tray as the CSystemTray object is created, or by using the member function CSystemTray::Create. eg.

CSystemTray m_TrayIcon;   // Member variable of some class
// in some member function maybe...
m_TrayIcon.Create(pParentWnd, WM_MY_NOTIFY, "Click here", 
                  hIcon, nTrayIconID);

This will insert an icon in the system tray. See the following section for details.

To MFC or not to MFC...

There are two forms of the class: MFC and Non-MFC. They are in the SystemTray.* and SystemTraySDK.* files respectively. The MFC version has been written to use MFC classes (CWnd etc) whereas the non-MFC will use HWND's. I may in future revise this so they both use HWND's for optimum compatibility.

Both classes have essentially the same functionality, excepting that the non-MFC version only supports a single tray icon per application. Because the non-MFC version is not derived from CWnd you need to be careful about the window you choose to receive the icon messages. If you set the parent of the icon as NULL, then the tray icon will handle it's own tray notification messages - but will also try and handle the menu commands sent from the context menu for the icon. To get around this you need to either:

  1. Set the parent of the tray icon as a window that will handle all tray icon notifications, or
  2. Set the parent as NULL, and use CSystemTray::SetTargetWnd to nominate the window that will receive the menu commands.

Eg. For a non-MFC tray icon, do the following:

CSystemTray m_TrayIcon;   // Member variable of some class
// in some member function maybe...
m_TrayIcon.Create(hInstance, NULL, WM_MY_NOTIFY, 
                  "Click here", hIcon, nID);

// Send all menu messages to hMyMainWindow


CSystemTray(CWnd* pWnd, UINT uCallbackMessage, LPCTSTR szToolTip,
            HICON icon, UINT uID, BOOL bHidden = FALSE,
            LPCTSTR szBalloonTip = NULL, LPCTSTR szBalloonTitle = NULL, 
            DWORD dwBalloonIcon = NIIF_NONE, UINT uBalloonTimeout = 10);
BOOL Create(CWnd* pWnd, UINT uCallbackMessage, LPCTSTR szToolTip, 
            HICON icon, UINT uID, BOOL bHidden = FALSE,
            LPCTSTR szBalloonTip = NULL, LPCTSTR szBalloonTitle = NULL, 
            DWORD dwBalloonIcon = NIIF_NONE, UINT uBalloonTimeout = 10);

The non-MFC version includes an additional parameter (parameter 1) that represents the applications instance handle.

Note that the destructor automatically removes the icon from the tray.

pWnd Window where notification messages will be sent. May be NULL
uCallbackMessage The notification messages that will be sent to the parent window
szToolTip Tooltip for the tray icon
icon Handle to the icon to be displayed
uID Tray icon ID
bHidden If TRUE, the icon is initially hidden
szBalloonTip The balloon tip text (Windows 2000 only)
szBalloonTitle The balloon tip title (Windows 2000 only)
dwBalloonIcon The balloon tip icon (Windows 2000 only)
uBalloonTimeout The balloon tip timeout (Windows 2000 only)

If the pWnd parameter is NULL then the function CSystemTray::OnTrayNotification will be called whenever the icon sends a notification message. See Default message handling for more details.


LRESULT OnTrayNotification(WPARAM wID, LPARAM lEvent) // Discussed below

void MoveToRight()           // Moves the icon to the far right of the tray, 
                             // so it is immediately to the left of  the clock 
void HideIcon()              // Hides but does not totally remove the icon
                             // from the tray. 
void RemoveIcon()            // Removes the icon from the tray (icon can no 
                             // longer be manipulated)
void ShowIcon()              // Redisplays a previously hidden icon
void AddIcon()               // Adds the icon to the tray   
void SetFocus(               // Sets the focus to the icon (Win2000 only)

BOOL ShowBalloon(LPCTSTR szText,     // Shows the balloon tip (Win2000 only)
                 LPCTSTR szTitle = NULL,   
                 DWORD dwIcon = NIIF_NONE, 
                 UINT uTimeout = 10);

BOOL    SetTooltipText(LPCTSTR pszTip) // Set Tooltip text
BOOL    SetTooltipText(UINT nID)       // Set tooltip from text resource ID
CString GetTooltipText() const         // Retrieve tool tip text
BOOL    SetNotificationWnd(CWnd* pWnd) // Self explanatory
CWnd*   GetNotificationWnd() const
BOOL    SetTargetWnd(CWnd* pTargetWnd);// Change or retrieve the window to
CWnd*   GetTargetWnd() const;          //  send menu commands to

BOOL    SetCallbackMessage(UINT uCallbackMessage) // Self explanatory
UINT    GetCallbackMessage() const;

HICON   GetIcon() const                // Get current tray icon
BOOL    SetIcon(HICON hIcon)           // Change tray icon. Returns FALSE 
                                       //  if unsuccessful
BOOL    SetIcon(LPCTSTR lpszIconName)  // Same, using name of the icon resource
BOOL    SetIcon(UINT nIDResource)      // Same, using icon resource ID

BOOL    SetStandardIcon(LPCTSTR lpIconName)  // Load icon from the
BOOL    SetStandardIcon(UINT nIDResource)    //   current application
// Set list of icons for animation 
BOOL    SetIconList(UINT uFirstIconID, UINT uLastIconID);
BOOL    SetIconList(HICON* pHIconList, UINT nNumIcons); 

// Start animation
BOOL    Animate(UINT nDelayMilliSeconds, int nNumSeconds = -1);   
BOOL    StepAnimation();                       // Step to next icon
BOOL    StopAnimation();                       // Stop animation

BOOL SetMenuDefaultItem(UINT uItem, BOOL bByPos);   // Set default menu item
void GetMenuDefaultItem(UINT& uItem, BOOL& bByPos); // Get default menu item
static void MinimiseToTray(CWnd* pWnd);
static void MaximiseFromTray(CWnd* pWnd, CRect rectTo);
static void MaximiseFromTray(CWnd* pWnd, LPCREATESTRUCT lpCreateStruct);

virtual void CustomizeMenu(CMenu*) // Customise the context menu before it's
                                   // displayed

SetStandardIcon can also take any of the following values (not available in WinCE):

nIDResource Description
IDI_APPLICATION Default application icon.
IDI_ASTERISK Asterisk (used in informative messages).
IDI_EXCLAMATION Exclamation point (used in warning messages).
IDI_HAND Hand-shaped icon (used in serious warning messages).
IDI_QUESTION Question mark (used in prompting messages).
IDI_WINLOGO Windows logo

The default CSystemTray message notification handler searches and displays the menu with the same ID as the tray icon. If you use this default handler then you can set the default menu item using SetMenuDefaultItem. The parameters uItem and bByPos are the same as those used in ::SetMenuDefaultItem.

The default menu item code was contributed by Enrico Lelina.

Minimising an application to the system tray

Two functions have been provided to allow you to easily "minimise" an application to the system tray:

    static void MinimiseToTray(CWnd* pWnd);
    static void MaximiseFromTray(CWnd* pWnd);

where pWnd is the window to minimise or maximise (usually your main application window).

"Minimising to the system tray" means that the applications main window is minimised using the DrawAnimatedRects function to make it appear that it is collapsing into the system tray. The main applications window is then made invisible and the applications icon is removed from the task bar. To minimise an application to the system tray simply call MinimiseToTray. System tray minimisation was inspired by Santosh Rao.

If an application has been minimised to the tray, then it can be maximised again by calling MaximiseFromTray. For example, if you have a CDialog derived class that you display in response to a user double-clicking on the tray icon (or selecting a menu item from the popup menu), then override the OnDestroy and OnShowWindow functions in your CDialog class and add the following lines:

void CMyDialog::OnDestroy() 

void CMyDialog::OnShowWindow(BOOL bShow, UINT nStatus) 
    CDialog::OnShowWindow(bShow, nStatus);
    if (bShow)

Icon animation

Icon animation can be achieved by specifying a list of icons using SetIconList(...), with either a range of icon resource IDs, or an array of HICONs and the size of the array. and calling Animate(UINT nDelayMilliSeconds, int nNumSeconds). The first parameter is the delay in milliseconds between each animation frame, and the second is the numer of seconds for which to animate the icon. If -1 is specified then animation will continue until StopAnimation() is called. Icon animation was suggested by Joerg Koenig.

Default message handling

The parent window, on receiving a notification message, can redirect this message back to the tray icon for handling by calling CSystemTray::OnTrayNotification(...). Alternatively, if the CSystemTray object was created with a NULL parent, then this function will be called whenver the icon sends a notification. The default implementation tries to find a menu with the same resource ID as the tray icon. If it finds a menu and the event received was a right mouse button up, then the submenu is displayed as a context menu. If a double click was received, then the message ID of first item in the submenu is sent back to the parent window.

Using the new Windows 2000 / IE5 Balloon tips

The new balloon tips rely on Shell32.dll to be version 5.0 or later, and for the the _WIN32_IE to be defined as 0x0500 or higher in your source code. There are two approaches you can use:

  • Assume that your users will have version 5.0 installed and add #define _WIN32_IE 0x0500

  • Do it properly and use DllGetVersion to get the version of Shell32.dll and adjust your code accordingly. Thanks to Porgee for quoting the MSDN tip:

    Use the DllGetVersion function to determine which Shell32.dll version is installed on the system. If it is version 5.0 or greater, initialize the cbSize member with:

    nid.cbSize = sizeof(NOTIFYICONDATA);

    Setting cbSize to this value enables all the version 5.0 and 6.0 enhancements. For earlier versions, the size of the pre-6.0
    structure is given by the NOTIFYICONDATA_V2_SIZE constant and the pre-5.0 structure is given by the NOTIFYICONDATA_V1_SIZE constant. Initialize the cbSize member with:

    nid.cbSize = NOTIFYICONDATA_V2_SIZE;

    Using this value for cbSize will allow your application to use NOTIFYICONDATA with earlier Shell32.dll versions, although without the version 6.0 enhancements.

I currently use the lazy method. There is a define ASSUME_IE5_OR_ABOVE in the system tray header that determines whether or not version 5.0 or above should be assumed present. Comment this out for applications targeting Shell32.dll versions less than 5.0.

Example of use

A good place to declare the tray icon is in your CFrameWnd derived class.


CSystemTray m_TrayIcon

Add a message map entry for the tray icon notification:

    ON_MESSAGE(WM_ICON_NOTIFY, OnTrayNotification)

Create the icon (maybe in your OnCreate):

if (!m_TrayIcon.Create(this, WM_ICON_NOTIFY, strToolTip, 
                       hIcon, IDR_POPUP_MENU))
    return -1;

where IDR_POPUP_MENU is the ID of a popup menu to display for the icon. You then need a handler for the tray icon notification message:

LRESULT CMainFrame::OnTrayNotification(WPARAM wParam, LPARAM lParam)
    // Delegate all the work back to the default 
        // implementation in CSystemTray.
    return m_TrayIcon.OnTrayNotification(wParam, lParam);

The menu used (IDR_POPUP_MENU) looks like the following:

        MENUITEM "&About...",      ID_APP_ABOUT
        MENUITEM "&Options...",    ID_APP_OPTIONS
        MENUITEM "E&xit",          ID_APP_EXIT

A single right click on the tray icon will bring up this menu, while a double click will send a ID_APP_ABOUT message back to the frame. (Note that in CE, ALT+Left button will bring up the menu, and ALT+Double Click will perform the default action).

Alternatively, you can m_TrayIcon.Create(NULL, ...) and leave out the message map entry for WM_ICON_NOTIFY. The default implementation of CSystemTray will take care of the rest.

NOTE on TrackPopupMenu

Many people have had troubles using TrackPopupMenu. They have reported that the popup menu will often not disappear once the mouse is clicked outside of the menu, even though they have set the last parameter of TrackPopupMenu() as NULL. This is a Microsoft "feature", and is by design. The mind boggles, doesn't it?

Anyway - to workaround this "feature", one must set the current window as the foreground window before calling TrackPopupMenu. This then causes a second problem - namely that the next time the menu is displayed it displays then immediately disappears. To fix this problem, you must make the currernt application active after the menu disappears. This can be done by sending a benign message such as WM_NULL to the current window.

So - what should have been a simple:

TrackPopupMenu(hSubMenu, TPM_RIGHTBUTTON, pt.x,pt.y, 0, hDlg, NULL);


TrackPopupMenu(hSubMenu, TPM_RIGHTBUTTON, pt.x,pt.y, 0, hDlg, NULL);
PostMessage(hDlg, WM_NULL, 0, 0);

Refer to KB article "PRB: Menus for Notification Icons Don't Work Correctly" for more info.


I updated the class so it would work in CE. A CE demo project is now included.

Thomas Mooney helped make changes that allows the class to be used in an NT service. The problem occured when using CSystemTray in an application that runs as a service on NT. When NT starts, the application starts. Trouble is, there is no taskbar, no system tray, nowhere to put the icon until someone has logged on. This has been fixed.

Michael Dun added Windows 2000 support - namely those way cool balloon tips.

The class now supports the new Windows2000 features, as well as CE (including popup menus from the tray). Two new functions for minimising an application to the system tray have also been added.

21 Sep 2000 - Matthew Ellis has improved the minimise-to-tray functionality by providing an improved version of the GetTrayWndRect function that searches for the location of the system tray. He has also provided a function GetDoWndAnimation that checks if the system has been set to show window animation (for minimise/maximise), and if not, no animation will be shown.

There is also a non-MFC version.

Håkan Trygg has updated the class with the following:

Instead of always sending the menu messages to the Main window (AfxMainWnd) they get sent to another, specified window. It's called the target window. If there is no specified window then AfxMainWnd is used.

The new functions are

    BOOL  SetTargetWnd(CWnd* pTargetWnd);
    CWnd* GetTargetWnd() const;

Also: the creation flags of the tray icon are saved so that if the icon needs to be recreated (settings change, taskbar recreated etc) then the icon will be created properly.

16 Jun 2002 Added the ASSUME_IE5_OR_ABOVE define and fixed VC 7.0 compile errors.

3 Aug 2003 - added a bunch of small fixes as well as the CustomizeMenu method. Thanks to Anton Treskunov for this and the fixes.


Håkan Trygg has also methods added to hold and change the menu.
    BOOL SetMenuText(UINT uiCmd, LPCTSTR szText);
    BOOL SetMenuText(UINT uiCmd, UINT uiID);
    BOOL CheckMenuItem(UINT uiCmd, BOOL bCheck);
    BOOL EnableMenuItem(UINT uiCmd, BOOL bEnable);
    BOOL DeleteMenu(UINT uiCmd);

The updated class files can be downloaded here. They have not been merged into the main class yet simply becuase I haven't had time to test - but I felt them important enough that I didn't want to delay making them available.


  • ghuiL
  • ghuiL
  • 2014年10月30日 10:25
  • 1763


原文地址:Qt之系统托盘(QSystemTrayIcon详解)作者:一去丶二三里    托盘是什么?这个就不用在多说了!而Qt中如何实现自己的托盘功能,Qt自带的demo也很明了,这里我们就来实现自己...
  • u010002704
  • u010002704
  • 2014年09月11日 15:18
  • 601

最新Android之支持多进程、多线程的TrayPreferences代替SharedPreferences 的总结

一、简单介绍TrayPreferences 1、介绍: 在github上看到了一个 android SharedPreferences 的替代方案Tray,学习了一下。特性基于ContentPr...
  • u012372365
  • u012372365
  • 2016年09月02日 16:37
  • 1250

Spark学习 基础函数功能详细说明 附代码和执行过程 上机学习详细说明

Spark学习 基础函数功能详细说明 附代码和执行过程log 上机学习详细说明
  • wyx100
  • wyx100
  • 2016年07月20日 21:42
  • 906

node-webkit 相关使用心得 --系统托盘(tray)的使用方法

最近公司开发桌面应用程序,要求使用node-webkit进行开发,对于从未涉及过这方面的菜鸟只能硬着头皮学习了几天,通过不断测试练习,偶有心得,在此写出来供需要的网友们参考应用。 下图是我的主要目录结...
  • xzs_123
  • xzs_123
  • 2015年06月25日 11:06
  • 2773


对于头文件中的CxxxDlg类定义而言,需要添加以下几个地方,见下图 对于上图中的最下面的public部分,略作解释...
  • darkdragonking
  • darkdragonking
  • 2014年11月27日 11:04
  • 1007


  • chuanren1991
  • chuanren1991
  • 2015年11月04日 11:06
  • 419


代码如下: 这是不可以的,会报错: 2016-08-18 11:36:00,267 [main] ERROR [org.mybatis.spring.mapper.MapperFactoryBea...
  • unix21
  • unix21
  • 2016年08月18日 11:48
  • 1881

linux命令(四):分析开机内核信息 dmesg

dmesg dmesg命令用于打印Linux系统开机启动信息,kernel会将开机信息存储在ring buffer中。您若是开机时来不及查看信息,可利用dmesg来查看(print or contr...
  • menogen
  • menogen
  • 2014年04月08日 20:43
  • 1936

Mybatis3源码分析(二):扫描Mapper关联到spring IOC容器

首先讲讲mapper是怎么从配置到对象的。         实现了basePackage下所有实现了markerInterface指明的类...
  • flashflight
  • flashflight
  • 2015年02月04日 01:36
  • 6885
您举报文章:Adding Icons to the System Tray