Taskbar Status Area Icons


Click here to change the theme.

Applications usually have a window that has an icon in the Taskbar, but the following is an explanation of the conditions in which a window does or does not have an icon in the Taskbar. The remainder of this page describes how to put an icon in the status area of the Taskbar.

Any window with the WS_VISIBLE style bit off is hidden and not shown in the taskbar. Otherwise if a window has the WS_EX_APPWINDOW extended style, the taskbar shows a button for the window and if it has the WS_EX_TOOLWINDOW extended style, the taskbar does not show a button for the window. A window should not have both of these extended styles. If a visible window has neither of these extended styles, a button is shown in the taskbar if the window is not owned and not shown if the window is owned.

To put an icon in the Status Area of the Taskbar (often called the System Tray), use Shell_NotifyIcon. See Taskbar for a description of the Taskbar, including the Status area.

Other Articles and Samples

I show a sample below of adding an icon to the in the status area of the Taskbar, but the following articles are also relevant.

My sample

In an MFC SDI or MDI application, you could put the following in the header of your CWinApp-derived class:

NOTIFYICONDATA m_NotifyIconData;

Then in your InitInstnace:

m_NotifyIconData.cbSize = sizeof m_NotifyIconData;
m_NotifyIconData.hWnd = m_pMainWnd->m_hWnd;
m_NotifyIconData.uID = 1;
m_NotifyIconData.uFlags = NIF_ICON|NIF_TIP;
m_NotifyIconData.hIcon = LoadIcon(IDR_MAINFRAME);
strcpy(m_NotifyIconData.szTip, AfxGetAppName());
Shell_NotifyIcon(NIM_ADD, &m_NotifyIconData);

And in your ExitInstnace you must remove the icon:

m_NotifyIconData.uFlags = 0;
Shell_NotifyIcon(NIM_DELETE, &m_NotifyIconData);

You will probably want to also use the WS_EX_TOOLWINDOW extended style for your main window. A window with WS_EX_TOOLWINDOW extended style does not appear in the Taskbar (the part of the Taskbar that most applications appear in). If you give a window the WS_EX_TOOLWINDOW extended style and do not use Shell_NotifyIcon to put it in the Status Area of the Taskbar, then an icon for the window does not appear in either the "Taskbar Buttons" or the "Status Area" of the Taskbar. Notice, though, that a window with the WS_EX_TOOLWINDOW extended style has a few differences from other windows; they do not have menu bars, system menus or minimize or maximize buttons, and there does not seem to be a way to get Windows to create any of those for them.

You might want to use CMiniFrameWnd instead of CFrameWnd for the main frame for your application. It appears that CMiniFrameWnd uses the WS_EX_TOOLWINDOW extended style by default.

Mouse Events

Unfortunately, that is not enough. The icon in the status area just sits there. If you minimize your application then it is not easy to get it back up. The NOTIFYICONDATA structure provides the opportunity to specify a message id for mouse events for the icon. It seems to be a necessity to process the mouse events. You can use either an application-defined message id (see WM_USER) or you can use a registered message id; I prefer registered messages. See Application-Defined Messages for a description of both types of message ids.

So assuming you are using a registered message id, at the beginning of the cpp files for both your CWinApp-derived class and your CMainFrame-derived class put:

static UINT NEAR WM_YOURTRAYCALLBACKMESSAGE =
    RegisterWindowMessage("YourTrayCallbackMessage");

Then in your InitInstance add the NIF_MESSAGE flag and add a line to set the message id in the  NOTIFYICONDATA structure, as in:

m_NotifyIconData.uFlags = NIF_ICON|NIF_TIP|NIF_MESSAGE;
m_NotifyIconData.uCallbackMessage = WM_YOURTRAYCALLBACKMESSAGE;

Then in your CMainFrame-derived class add a message-map entry for processing the registered message:

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    //{{AFX_MSG_MAP(CMainFrame)
    ON_WM_CREATE()
    //}}AFX_MSG_MAP
    ON_REGISTERED_MESSAGE(WM_YOURTRAYCALLBACKMESSAGE, OnTrayCallbackMessage)
END_MESSAGE_MAP()

The following is a sample function for processing the mouse events; it is just the basics:

afx_msg LRESULT CMainFrame::OnTrayCallbackMessage(WPARAM wParam, LPARAM lParam) {
UINT uID = (UINT) wParam;
UINT uMouseMsg = (UINT) lParam;
if (uMouseMsg == WM_LBUTTONDOWN || uMouseMsg == WM_RBUTTONDOWN)
    ActivateFrame();
return 0;
}

You will also need to add an entry in your CMainFrame-derived class header file's message map:

// Generated message map functions
protected:
    //{{AFX_MSG(CMainFrame)
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    //}}AFX_MSG
    afx_msg LRESULT OnTrayCallbackMessage(WPARAM, LPARAM);
    DECLARE_MESSAGE_MAP()