载入中,请稍候……

VC系统托盘编程

Admin 于 2008-09-20 16:34:47 发表C/C++

订阅: http://www.miniboke.com/Feed/Article_34.aspx
引用: http://www.miniboke.com/Trackback/iAgEveSepUuRllbBSBOZ.aspx (UTF-8)
VC++中网页内容提取范例:提取企业黄页信息 < VC系统托盘编程 > 使用SC命令进行Windows服务卸载

现在比较流行的软件都会在系统托盘上显示一个图标,左键点击该图标时可以显示/隐藏软件主界面,节省任务栏的空间;右键点击该图标会出现一个菜单,显示一些常用的功能。那么这是怎么实现的呢?其实很简单,只需要一个API函数就搞定了,跟我一起看吧~
首先看一个结构体:

  1. typedef struct _NOTIFYICONDATA { 
  2.     DWORD cbSize; 
  3.     HWND hWnd; 
  4.     UINT uID; 
  5.     UINT uFlags; 
  6.     UINT uCallbackMessage; 
  7.     HICON hIcon; 
  8.     TCHAR szTip[64]; 
  9.     DWORD dwState; //Version 5.0 
  10.     DWORD dwStateMask; //Version 5.0 
  11.     TCHAR szInfo[256]; //Version 5.0 
  12.     union { 
  13.         UINT  uTimeout; //Version 5.0 
  14.         UINT  uVersion; //Version 5.0 
  15.     } DUMMYUNIONNAME; 
  16.     TCHAR szInfoTitle[64]; //Version 5.0 
  17.     DWORD dwInfoFlags; //Version 5.0 
  18. } 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)
然后对该结构的成员进行初始化:

  1. void InitTray() 
  2.   //初始化m_nid 
  3.   m_nid.cbSize = sizeof(NOTIFYICONDATA); 
  4.   m_nid.hWnd = this->m_hWnd; 
  5.   m_nid.uID = IDR_TRAYMENU; 
  6.   m_nid.uFlags = NIF_ICON|NIF_TIP|NIF_MESSAGE; 
  7.   m_nid.hIcon = AfxGetApp()->LoadIcon(IDI_RED); 
  8.   strcpy (m_nid.szTip, "我的托盘听我的"); 
  9.   m_nid.uCallbackMessage = UM_TRAYNOTIFICATION; 

上面的程序我就不多说了,相信大家都能看懂。
(2)添加/修改/移除图标
这个更简单,直接调用Shell_NotifyIcon函数就可以了:

  1. //向托盘添加图标 
  2. void AddTray() 
  3.   Shell_NotifyIcon(NIM_ADD, &m_nid); 
  4. //移除托盘图标,在程序退出时一定要记得调用,否则图标会残留在托盘上 
  5. void RemoveTray() 
  6.   Shell_NotifyIcon(NIM_DELETE, &m_nid); 
  7. //修改图标,QQ登陆时托盘上的动态效果不用我教了吧 
  8. void ModifyTray(UINT uId)//参数为要显示的Icon的ID号 
  9.   m_nid.hIcon = AfxGetApp()->LoadIcon(uId); 
  10.   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文件里实现该函数:

  1. LRESULT CXXXDlg::OnTrayNotification(WPARAM wId, LPARAM lEvent) 
  2.   if(wId!=m_nid.uID 
  3.    || (lEvent!=WM_LBUTTONUP && lEvent!=WM_RBUTTONUP)) 
  4.    return 0; 
  5.  
  6.   //加载菜单 
  7.   CMenu menu; 
  8.   if(!menu.LoadMenu(wId)) 
  9.        return 0; 
  10.   //获取弹出菜单 
  11.   CMenu *pSubMenu = menu.GetSubMenu(0); 
  12.   if(!pSubMenu) 
  13.        return 0; 
  14.  
  15.   if(lEvent == WM_RBUTTONUP) 
  16.   { 
  17.         //设置默认菜单项 
  18.         ::SetMenuDefaultItem(pSubMenu->m_hMenu, 0, TRUE); 
  19.  
  20.         //获取鼠标位置 
  21.         CPoint mouse; 
  22.         GetCursorPos(&mouse); 
  23.  
  24.         //设置快捷菜单 
  25.         ::SetForegroundWindow(m_nid.hWnd); 
  26.         ::TrackPopupMenu(pSubMenu->m_hMenu, 0, mouse.x, mouse.y, 0, m_nid.hWnd, NULL); 
  27.   } 
  28.   else 
  29.   { 
  30.         ::SendMessage(m_nid.hWnd, WM_COMMAND, pSubMenu->GetMenuItemID(0), 0); 
  31.   } 
  32.  
  33.   return 1;  

下面逐句解释上面这段代码:
首先判断发送该消息的图标的ID是否等于m_nid中的ID号,再判断接收到的鼠标消息是否为左键单击消息WM_LBUTTONUP或右键单击消息WM_RBUTTONUP,如果不是则不对该消息进行响应。
然后加载菜单,右键单击时显示该菜单。先用LoadMenu()加载顶层菜单,再用GetSubMenu()获得一级子菜单。
当鼠标事件为右键单击时,显示菜单,否则(即左键单击时)用::SendMessage()向接收窗口发送命令消息,进行相应的处理。::SetMenuDefaultItem()用于设置菜单的默认选择项,以粗体显示。最后用::TrackPopupMenu()函数显示右键菜单。该菜单可能不会像通常那样马上消失,这是因为从弹出菜单接收消息的窗口必须是前景窗口。调用::SetForegroundWindow()函数就可以纠正该错误。
(4)添加菜单消息处理函数
在右键菜单中选择一项后,就会发送命令消息WM_COMMAND,在ClassWizard里添加WM_COMMAND的消息处理函数:

  1. BOOL CZYJReportDlg::OnCommand(WPARAM wParam, LPARAM lParam) 
  2.   // TODO: Add your specialized code here and/or call the base class 
  3.   switch(LOWORD(wParam)) 
  4.   { 
  5.   case ID_SHOWHIDE: 
  6.         ShowWindow(m_bShow?SW_HIDE:SW_SHOW); 
  7.         m_bShow = !m_bShow; 
  8.         break
  9.   case ID_EXIT: 
  10.         RemoveTray(); 
  11.         PostQuitMessage(0); 
  12.         break
  13.   case ID_ABOUT: 
  14.         CAboutDlg about; 
  15.         about.DoModal(); 
  16.         break
  17.   } 
  18.  
  19.   return CDialog::OnCommand(wParam, lParam); 

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

被阅804次, 0投一票

安徽舞台

2010-07-09 14:38:11
还真是这样的呢!
  • 看完了要说点啥么?
  • 昵称 (不填说不了话)
  • 信箱地址 (不会被公开,但是不填也说不了话)
  • 网址 (这个不填也成)

Powered by MiniBoke v2.0.0.8 Build 0828

Copyright © 2008 迷你博客. All rights reserved.

粤ICP备07500939号