VC系统托盘编程指南

现在比较流行的软件都会在系统托盘上显示一个图标,左键点击该图标时可以显示/隐藏软件主界面,节省任务栏的空间;右键点击该图标会出现一个菜单,显示一些常用的功能。那么这是怎么实现的呢?其实很简单,只需要一个API函数就搞定了,跟我一起看吧~
首先看一个结构体:
typedef struct _NOTIFYICONDATA {
    DWORD cbSize;
    HWND hWnd;
    UINT uID;
    UINT uFlags;
    UINT uCallbackMessage;
    HICON hIcon;
    TCHAR szTip[64];
    DWORD dwState; //Version 5.0
    DWORD dwStateMask; //Version 5.0
    TCHAR szInfo[256]; //Version 5.0
    union {
        UINT  uTimeout; //Version 5.0
        UINT  uVersion; //Version 5.0
    } DUMMYUNIONNAME;
    TCHAR szInfoTitle[64]; //Version 5.0
    DWORD dwInfoFlags; //Version 5.0
} NOTIFYICONDATA, *PNOTIFYICONDATA;
使用该结构指定托盘图标的相关信息,其中Version 5.0的部分表示的是Shell DLL的版本号,可以用DllGetVersion获得当前系统的DLL版本。出于兼容的考虑,该部分很少使用。
cbSize: NOTIFYICONDATA结构的大小,sizeof(NOTIFYICONDATA)
hWnd:接收托盘通知的窗口句柄
uID:托盘图标的ID号,该ID与hWnd一起唯一标识了一个托盘图标的身份,因此该ID号不能重复,一般使用与该图标相关联的菜单的ID
uFlags:为下列值的组合
NIF_ICON:表示结构中的hIcon有效 
NIF_MESSAGE:表示结构中的uCallbackMessage有效
NIF_TIP:表示结构中的szTip有效
NIF_STATE:表示结构中的dwState和dwStateMask有效
NIF_INFO:使用气球型的托盘提示代替传统的方框型托盘提示.结构中的szInfo, uTimeout, szInfoTitle,和dwInfoFlags有效
uCallbackMessage:托盘消息,当托盘区域有鼠标事件(如鼠标移动,单击等)产生时,会向接收窗口发送该消息,进行相应的处理
hIcon:托盘图标句柄
szTip:托盘提示字符
好了,就介绍这么多,下面接收这个API函数Shell_NotifyIcon:
BOOL Shell_NotifyIcon(
    DWORD dwMessage,
    PNOTIFYICONDATA pnid
);
dwMessage:为下列值之一
NIM_ADD:添加托盘图标
NIM_DELETE:删除图盘图标
NIM_MODIFY:修改托盘图标
NIM_SETFOCUS:Version 5.0
NIM_SETVERSION:Version 5.0
pnid:NOTIFYICONDATA结构的指针

好了,下面言归正传,开始着手编程了~
我们分为以下几步逐步分析:
 初始化
 添加/修改/移除图标
 添加托盘消息响应函数
 添加菜单消息处理函数
(1) 初始化
首先要声明一个NOTIFYICONDATA成员变量:
NOTIFYICONDATA m_nid;
然后在资源面板中添加一个菜单资源IDR_TRAYMENU和一个图标资源IDI_RED
由于我们要处理鼠标消息,因此先定义一个用户自定义消息:
 #define UM_ TRAYNOTIFICATION (WM_USER+100)
然后对该结构的成员进行初始化:
void InitTray()
{
 //初始化m_nid
 m_nid.cbSize = sizeof(NOTIFYICONDATA);
 m_nid.hWnd = this->m_hWnd;
 m_nid.uID = IDR_TRAYMENU;
 m_nid.uFlags = NIF_ICON|NIF_TIP|NIF_MESSAGE;
 m_nid.hIcon = AfxGetApp()->LoadIcon(IDI_RED);
 strcpy (m_nid.szTip, "我的托盘听我的");
 m_nid.uCallbackMessage = UM_TRAYNOTIFICATION;
}
上面的程序我就不多说了,相信大家都能看懂。
(2) 添加/修改/移除图标
这个更简单,直接调用Shell_NotifyIcon函数就可以了:
//向托盘添加图标
void AddTray()
{
 Shell_NotifyIcon(NIM_ADD, &m_nid);
}
//移除托盘图标,在程序退出时一定要记得调用,否则图标会残留在托盘上
void RemoveTray()
{
 Shell_NotifyIcon(NIM_DELETE, &m_nid);
}
//修改图标,QQ登陆时托盘上的动态效果不用我教了吧?
void ModifyTray(UINT uId)//参数为要显示的Icon的ID号
{
 m_nid.hIcon = AfxGetApp()->LoadIcon(uId);
 Shell_NotifyIcon(NIM_MODIFY, &m_nid);
}
(3) 添加托盘消息响应函数
这个其实就是响应自定义消息,忘了?好,那我们一起复习一下吧~
首先定义消息标识符(第1步已经定义过了):
#define UM_ TRAYNOTIFICATION (WM_USER+100)
然后在头文件的DECLARE_MESSAGE_MAP()之前定义消息响应函数:
afx_msg LRESULT OnTrayNotification(WPARAM wId, LPARAM lEvent);
其中两个参数分别为图标ID号和鼠标事件
最后在cpp文件里实现该函数:
LRESULT CXXXDlg::OnTrayNotification(WPARAM wId, LPARAM lEvent)
{
 if(wId!=m_nid.uID
  || (lEvent!=WM_LBUTTONUP && lEvent!=WM_RBUTTONUP))
  return 0;

 //加载菜单
 CMenu menu;
 if(!menu.LoadMenu(wId))
  return 0;
 //获取弹出菜单
 CMenu *pSubMenu = menu.GetSubMenu(0);
 if(!pSubMenu)
  return 0;

 if(lEvent == WM_RBUTTONUP)
 {
  //设置默认菜单项
  ::SetMenuDefaultItem(pSubMenu->m_hMenu, 0, TRUE);

  //获取鼠标位置
  CPoint mouse;
  GetCursorPos(&mouse);

  //设置快捷菜单
  ::SetForegroundWindow(m_nid.hWnd);
  ::TrackPopupMenu(pSubMenu->m_hMenu, 0, mouse.x, mouse.y, 0, m_nid.hWnd, NULL);
 }
 else
 {
  ::SendMessage(m_nid.hWnd, WM_COMMAND, pSubMenu->GetMenuItemID(0), 0);
 }

 return 1;
}
下面逐句解释上面这段代码:
首先判断发送该消息的图标的ID是否等于m_nid中的ID号,再判断接收到的鼠标消息是否为左键单击消息WM_LBUTTONUP或右键单击消息WM_RBUTTONUP,如果不是则不对该消息进行响应。
然后加载菜单,右键单击时显示该菜单。先用LoadMenu()加载顶层菜单,再用GetSubMenu()获得一级子菜单。
当鼠标事件为右键单击时,显示菜单,否则(即左键单击时)用::SendMessage()向接收窗口发送命令消息,进行相应的处理。::SetMenuDefaultItem()用于设置菜单的默认选择项,以粗体显示。最后用::TrackPopupMenu()函数显示右键菜单。该菜单可能不会像通常那样马上消失,这是因为从弹出菜单接收消息的窗口必须是前景窗口。调用::SetForegroundWindow()函数就可以纠正该错误。
(4) 添加菜单消息处理函数
在右键菜单中选择一项后,就会发送命令消息WM_COMMAND,在ClassWizard里添加WM_COMMAND的消息处理函数:
BOOL CZYJReportDlg::OnCommand(WPARAM wParam, LPARAM lParam)
{
 // TODO: Add your specialized code here and/or call the base class
 switch(LOWORD(wParam))
 {
 case ID_SHOWHIDE:
  ShowWindow(m_bShow?SW_HIDE:SW_SHOW);
  m_bShow = !m_bShow;
  break;
 case ID_EXIT:
  RemoveTray();
  PostQuitMessage(0);
  break;
 case ID_ABOUT:
  CAboutDlg about;
  about.DoModal();
  break;
 }

 return CDialog::OnCommand(wParam, lParam);
}
其中第一个参数wParam的低字节LOWORD(wParam)标识了所选子菜单的ID,根据所选的菜单项执行相应的程序。
OK,以上介绍了写托盘程序的完整过程,是不是很简单呢?当然托盘编程的内容还远不止这些,比如Version 5.0的部分,还可以添加很多漂亮的效果,欢迎大家跟我一起探讨!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值