Shell_NotifyIcon函数用于向Windows系统任务栏的状态区域发送消息,原型如下:
BOOL Shell_NotifyIcon(
  __in  DWORD dwMessage,
  __in  PNOTIFYICONDATA lpdata
); 
 
其中
(1)参数dwMessage表示该函数要执行的操作,取值如下:
NIM_ADD:往状态区添加一个图标;在参数lpdata指向的结构中的参数uID或guidItem给这个图标指定了标识;
NIM_MODIFY:修改状态区的一个图标;使用的是NIM_ADD中指定的图标标识;
NIM_DELETE:删除状态区的一个图标;使用的是NIM_ADD中指定的图标标识;
NIM_SETFOCUS:将焦点重新设给任务栏的通知区域;一般通知区域的图标在完成UI操作时使用该函数来重新获取焦点,例如通知区域的图标显示一个快捷菜单,但用户按ESC键取消该菜单的显示时,图标就可以使用NIM_SETFOCUS来重新使通知区域获得焦点;
NIM_SETVERSION:指示通知区域要遵循参数lpdata指向的结构中的参数uVersion指定的版本来操作,版本号指定了哪些成员是可识别的。
 
(2)参数lpdata是指向NOTIFYICONDATA结构的指针,该结构的内容取决于参数dwMessage的值,例如可以定义一个图标用于添加到通知区域,以用来显示通知;也可以指定一个用于修改或删除的图标。
 
返回值:
成功返回TRUE,失败返回FALSE;当dwMessage设置为MIN_SETVERSION时,该函数返回TRUE表示版本号更改成功,返回FALSE表示系统不支持指定的版本。
 
注意:对于Windows 2000(Shell32.dll version 5.0),Shell_NotifyIcon对于鼠标和键盘事件的处理与早期的操作系统的不同点在于:
(1)用户使用键盘选择了通知图标的快捷菜单,Shell将发送WM_CONTEXTMENU消息给图标对应的应用程序,而早期操作系统则发送WM_RBUTTONDOWN和WM_RBUTTONUP消息;
(2)用户使用键盘选择通知图标,并使用空格键或Enter键激活它,则Shell将发送NIN_KEYSELECT通知给应用程序,而早期版本则发送WM_RBUTTONDOWN和WM_RBUTTONUP消息;
(3)用户使用鼠标选择通知图标,并使用Enter键激活它,Shell将发送NIN_SELECT通知给应用程序,而早期版本发送WM_RBUTTONDOWN和WM_RBUTTONUP消息;
 
对于Windows XP(Shell32.dll version 6.0),当用户将鼠标指向关联着气泡通知的图标时,Shell将发送下列消息:
(1)NIN_BALLOONSHOW:当气泡显示时发送(气泡在队列中排队);
(2)NIN_BALLOONHIDE:当气泡消失时发送,例如,当图标删除时。在气泡因为超时或者用户鼠标单击后消失时,不发送该消息;
(3)NIN_BALLOONTIMEOUT:气泡超时后消失时发送;
(4)NIN_BALLOONUSERCLICK:用户鼠标单击气泡使气泡消失时发送;
 
结构NOTIFYICONDATA包含了Windows系统在通知区域显示通知所需的信息,本函数Shell_NotifyIcon使用,原型如下:
typedef struct _NOTIFYICONDATA {
  DWORD cbSize; //结构的大小
  HWND  hWnd; //接收图标通知的窗口
  UINT  uID; //任务栏通知区域图标的标识
  UINT  uFlags; //指定其他哪些参数有效,是如下取值的组合:
    //NIF_MESSAGE:uCallbackMessage参数有效;
    //NIF_ICON:hIcon参数有效;NIF_TIP:szTip参数有效;
    //NIF_STATE:dwState和dwStateMask参数有效;
    //NIF_INFO:显示气泡通知,szInfo,szInfoTitle,dwInfoFlags和
    //uTimeout参数有效;
    //NIF_GUID、NIF_REALTIME、NIF_SHOWTIP
  UINT  uCallbackMessage;
  HICON hIcon; //图标句柄
  TCHAR szTip[64]; //提示字符串
  DWORD dwState;  //图标的状态:NIS_HIDDEN(图标隐藏)、
      //NIS_SHAREICON(该图标资源被多个图标共享)
  DWORD dwStateMask; //dwState的掩码
  TCHAR szInfo[256]; //图标气泡显示的字符串
  union {
    UINT uTimeout; //超时的时间
    UINT uVersion; //图标的版本
  } ;
  TCHAR szInfoTitle[64]; //图标气泡的标题
  DWORD dwInfoFlags;//图标气泡的类型:NIIF_NONE,无类型;
      //NIFF_INFO,通知类型;NIFF_WARNING:警告类型;
      //NIFF_ERROR,错误类型;
//NIFF_USER,在Windows XP中表示使用hIcon参数指定的图标
//在Windows Vista及后续版本,表示使用hBalloonIcon的图标
//NIFF_NOSOUND、NIIF_LARGE_ICON、
    //NIFF_RESPECT_QUIET_TIME、NIFF_ICON_MASK
  GUID  guidItem; //在Windows XP和Vista中,保留为0;在Windows 7及后续版本,
    //用于标识一个图标的注册GUID值
  HICON hBalloonIcon; //用于Windows Vista及后续版本中,表示自定义气泡图标类型
} NOTIFYICONDATA, *PNOTIFYICONDATA;
 
上面两个函数使用的实例当然是在编写系统托盘程序时,下面这个类摘自金山开源作品之ARP防火墙,先看头文件吧:
#pragma once
//
#define WM_ARPFW_SYSTRAY      (WM_USER+100)
#define WM_ARPFW_SETICON      (WM_USER+101)
//
class CArpTray : public CWindowImpl<CArpTray, CWindow, CFrameWinTraits> 
{
public:
    CArpTray();
    ~CArpTray();
    DECLARE_WND_CLASS(_T("KSafeArpTray"))
    BEGIN_MSG_MAP(CArpTray)
        MSG_WM_CREATE(OnCreate)
        MSG_WM_DESTROY(OnDestroy)
        MSG_WM_CLOSE(OnClose)
        MSG_WM_COMMAND(OnCommand)
        MESSAGE_HANDLER(WM_ARPFW_SYSTRAY, OnSysTray)
        MESSAGE_HANDLER(WM_ARPFW_SETICON, OnSetIcon)
    END_MSG_MAP()
    LRESULT OnSysTray(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
    LRESULT OnSetIcon(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
    int OnCreate(LPCREATESTRUCT lpCreateStruct);
    void OnDestroy();
    void OnClose();
    void OnCommand(UINT uNotifyCode, int nID, CWindow wndCtl);
    void OnShowMain();
    void OnExit();
    void OnEnable(BOOL bEnable);
    void InitTrayIconStruct();
    void ShowTrayMenu();
    NOTIFYICONDATAW m_trayIcon;
    BOOL m_bArpFwEnable;
    HICON m_hIcons[2];
};
// 
接下来就是实现文件:
#include "stdafx.h"
#include "arpfw.h"
#include "tray.h"
#include "resource.h"
//
CArpTray::CArpTray()
{
  //加载防火墙开启和关闭时显示用的图标
    m_hIcons[0] = LoadIcon(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDI_ARP_ENABLE));
    m_hIcons[1] = LoadIcon(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDI_ARP_DISABLE));
}
CArpTray::~CArpTray()
{
}
//
/*********************************************
* 初始化系统托盘图标数据结构
*********************************************/
void CArpTray::InitTrayIconStruct() 
{
    USES_CONVERSION;
    CString strArpFw;
    HICON show_icon = NULL;
    RtlZeroMemory(&m_trayIcon, sizeof(m_trayIcon));
    m_trayIcon.cbSize = NOTIFYICONDATA_V1_SIZE;
    m_trayIcon.hWnd = m_hWnd;
    m_trayIcon.uID = 1;
  //使参数uCallbackMessage、hIcon、szTip有效
    m_trayIcon.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
    if (m_bArpFwEnable) 
    {
        m_trayIcon.hIcon = m_hIcons[0];
        strArpFw = _T("金山卫士ARP防火墙 - 已开启");
    } 
    else
    {
        m_trayIcon.hIcon = m_hIcons[1];
        strArpFw = _T("金山卫士ARP防火墙 - 未开启");
    }
    m_trayIcon.uCallbackMessage = WM_ARPFW_SYSTRAY;
    StringCchCopyW(m_trayIcon.szTip, ARRAYSIZE(m_trayIcon.szTip), strArpFw);
}
/***********************************************************************************
* 设置系统托盘图标
***********************************************************************************/
LRESULT CArpTray::OnSetIcon(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 
{
    m_bArpFwEnable = (BOOL)wParam;
    CString strArpFw;
    if (m_bArpFwEnable)
    {
        m_trayIcon.hIcon = m_hIcons[0];
        strArpFw = _T("金山卫士ARP防火墙 - 已开启");
    } 
    else
    {
        m_trayIcon.hIcon = m_hIcons[1];
        strArpFw = _T("金山卫士ARP防火墙 - 未开启");
    }
    StringCchCopyW(m_trayIcon.szTip, ARRAYSIZE(m_trayIcon.szTip), strArpFw);
    Shell_NotifyIcon(NIM_MODIFY, &m_trayIcon); //修改托盘图标
    return 0L;
}
/*****************************************************
* 系统初始化
*****************************************************/
int CArpTray::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
    RECT rc = {0, 0, 0, 0};
    SetWindowPos(NULL, &rc, SWP_HIDEWINDOW);  //隐藏窗口
    SetWindowText(_T(""));
    InitTrayIconStruct();
    Shell_NotifyIconW(NIM_ADD, &m_trayIcon); //添加托盘图标
    return 0;
}
/**********************************************
* 系统退出
**********************************************/
void CArpTray::OnDestroy() 
{
  //删除托盘图标
    Shell_NotifyIcon(NIM_DELETE, &m_trayIcon); 
  //关闭主窗口
    _Module.CloseMain();
}
void CArpTray::OnClose() 
{
    DestroyWindow();
}
/*******************************************************************
* 系统托盘收到消息
*******************************************************************/
LRESULT CArpTray::OnSysTray(UINT uMsg, WPARAM wParam, LPARAM lParam, 
                           BOOL& bHandled) 
{
    switch (lParam) 
    {
    case WM_RBUTTONUP:  //鼠标右键弹起则显示上下文菜单
        ShowTrayMenu();
        break;
    case WM_LBUTTONUP:  //鼠标左键弹起则显示主窗口
        _Module.ShowMain();
        break;
    }
    return 0;
}
/******************************************************************
* 收到命令消息后的处理
******************************************************************/
void CArpTray::OnCommand(UINT uNotifyCode, int nID, CWindow wndCtl) 
{
    switch (nID) 
    {
    case ID_ARPFW_SHOWMAIN: //显示主窗口
        OnShowMain();
        break;
    case ID_ARPFW_ENABLE: //启动防火墙
        OnEnable(TRUE);
        break;
    case ID_ARPFW_DISABLE:  //关闭防火墙
        OnEnable(FALSE);
        break;
    case ID_ARPFW_EXIT: //退出系统
        OnExit();
        break;
    }
}
void CArpTray::OnEnable(BOOL bEnable)
{
    _Module.EnableArpFw(bEnable);
}
void CArpTray::OnShowMain()
{
    _Module.ShowMain();
}
/***************************************
* 退出系统
***************************************/
void CArpTray::OnExit()
{
    CString strText;
    CString strTitle;
    int nRetCode;
    strText = _T("您确认要退出金山卫士ARP防火墙吗?");
    strTitle = _T("金山卫士ARP防火墙");
    nRetCode = MessageBox(strText, strTitle, MB_ICONQUESTION|MB_OKCANCEL);
    if (IDOK == nRetCode)
    {
        PostMessage(WM_CLOSE, 0, 0);
    }
}
/*************************************
* 显示系统托盘上下文菜单
*************************************/
void CArpTray::ShowTrayMenu() 
{
    POINT pos;
    CMenu menu;
    HMENU hMenu = NULL;
    menu.LoadMenu(IDR_TRAY_MENU);
    hMenu = menu.GetSubMenu(0);
  RemoveMenu(hMenu, m_bArpFwEnable ? ID_ARPFW_ENABLE : ID_ARPFW_DISABLE, MF_BYCOMMAND);
    GetCursorPos(&pos);  //获得当前光标位置,在此处显示菜单
    TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, pos.x, pos.y, 0, m_hWnd, NULL);
}
//