关于系统托盘图标类,网上也有很多的代码,但都不太简洁灵活易用,为了这一目的,本人自行封装了一个API版本的实现类,这个类的设计思想来源于观察者模式,从而较好地解耦了托盘通知消息的发送、接收、处理这三者间的关系,使这三者可以是同一个对象,也可以是不同的对象,具体的情况可根据业务逻辑灵活选择控制,主要包括以下几方面的特点: 1)对于托盘通知消息的接收处理,提供了一个默认常用的实现,也就是通知消息接收由托盘类对象本身接收,消息事件的处理也由托盘对象本身处理,双击图标显示某个窗口,右键单击图标显示快捷菜单。 2)支持事件处理扩展,有两种方法,一是提供一个接口设置消息事件处理器,从而将处理逻辑转到其它对象,这个对象要支持消息事件处理器接口约定;二是提供消息事件处理的虚函数回调,可由派生类重写定制。 3)托盘图标ID的管理,ID为UINT类型整数,一个对象对应一个ID,每当对象创建时,分配一个,从1开始,为防止冲突造成托盘图标创建失败,当对象销毁时,并不递减ID计数,因此ID一直是递增的,这也是最简单的方案。 4)支持内部或外部窗口来接收通知消息,这两种模式可以随时相互转换,一个托盘对象只对应一个窗口,多个托盘对象可以对应同一个窗口。如果是内部窗口,那么内部窗口是全局的有且仅有一个,由其窗口过程根据托盘ID将消息分发到对应的托盘对象。如果是外部窗口,那么可以由外部窗口自己处理,也可以调用托盘类的方法转到其它对象处理。
下面来看下基本托盘图标类的接口定义: 1 #ifndef _BASIC_TRAYICON_H 2 #define _BASIC_TRAYICON_H 3 4 #include "trayicon_msg_handler.h" 5 #include <shellapi.h> 6 #include <map> 7 8 extern const UINT WM_TRAY_NOTIFY_MSG; 9 10 class CBasicTrayIcon : public CTrayMsgDefaultHandler 11 { 12 public: 13 CBasicTrayIcon(); 14 virtual ~CBasicTrayIcon(); 15 16 bool Create(HWND hNotifyWnd=NULL); 17 void Destroy(); 18 19 bool SetNotifyWnd(HWND hWnd); 20 void SetMsgHandler(ITrayMsgHandler* pMsgHandler); 21 22 bool SetIcon(HICON hIcon); 23 bool SetIcon(LPCTSTR lpIconName); 24 bool SetIcon(UINT uID); 25 bool SetTooltipText(LPCTSTR lpText); 26 bool SetTooltipText(UINT uID); 27 28 UINT GetID() const; 29 HICON GetIcon() const; 30 LPCTSTR GetTooltipText() const; 31 32 void Show(); 33 void Hide(); 34 35 public: 36 static CBasicTrayIcon* FromID(UINT uID); 37 void OnNotify(UINT uEvent); 38 39 private: 40 static LRESULT CALLBACK NotifyWndProc(HWND, UINT, WPARAM, LPARAM); 41 static bool RegisterNotifyWndClass(); 42 static bool CreateNotifyWindow(); 43 static HWND s_hNotifyWnd; 44 static UINT s_uIDCount; 45 static std::map<UINT,CBasicTrayIcon*> s_trayicons; 46 47 private: 48 NOTIFYICONDATA m_tnd; 49 ITrayMsgHandler* m_pMsgHandler; 50 }; 51 52 #endif 从上可看出,CBasicTrayIcon从通知消息事件默认处理器CTrayMsgDefaultHandler继承,也就是说CBasicTrayIcon类已实现了消息事件的默认处理。下面说明几个重要的类方法:(1)Create---创建托盘图标,请注意这里只是创建,没有关联图标,因此在任务栏上并不会显示,要显示需要接着调用SetIcon系列方法,参数hWnd为托盘通知消息接收窗口,默认为NULL,当为NULL的时候,则会创建一个内部隐藏的窗口来接收消息,否则由其指定的外部窗口来接收托盘通知消息。如果成功那么返回true,否则返回false。(2)Destroy---销毁托盘图标。(3)SetNotifyWnd---设置通知消息接收窗口,当参数hWnd为NULL时,则转移到内部窗口接收通知消息,否则转为其指定的外部窗口接收通知消息。因此,这个方法实现了特点4。(4)SetMsgHandler---设置托盘通知消息处理器,参数pMsgHandler为ITrayMsgHandler指针类型,当其为NULL时,则处理为其指定的消息事件处理器,否则为托盘对象自己。因此,这个方法实现了特点2。(5)SetIcon---设置托盘图标,有3个重载形式,如果成功那么返回true,否则返回false。(6)SetToolipText---设置提示文本,有2个重载形式,如果成功返回true,否则返回false。(7)Show---显示托盘图标,具体的内部实现根据_WIN_IE的版本而定,如果成功返回true,否则返回fasle。(8)Hide---隐藏托盘图标,具体的内部实现根据_WIN_IE的版本而定,如果成功返回true,否则返回fasle。(9)FromID---这是个静态方法,从ID转换为托盘对象,如果成功那么返回对应的指针,否则返回NULL。(10)OnNotify---通知消息事件分发处理。公开方法9和10,是为了支持由外部窗口接收通知消息时可以转交给其它对象的灵活性(见特点4)。这个类默认实现消息的处理者为托盘对象自己,即m_pMsgHandler在构造时指向它自己。
接下来,看下消息事件处理器的接口定义:
1 #ifndef _TRAYICON_MSG_HANDLER_H 2 #define _TRAYICON_MSG_HANDLER_H 3 4 struct ITrayMsgHandler 5 { 6 virtual ~ITrayMsgHandler() {} 7 virtual void OnMouseMove() {} 8 virtual void OnLButtonDown() {} 9 virtual void OnLButtonDblclk() {} 10 virtual void OnRButtonUp() {} 11 virtual void OnRButtonDown() {} 12 }; 13 14 class CTrayMsgDefaultHandler : public ITrayMsgHandler 15 { 16 public: 17 CTrayMsgDefaultHandler(); 18 virtual ~CTrayMsgDefaultHandler(); 19 20 bool SetMenu(UINT uID); 21 bool SetMenu(LPCTSTR lpName); 22 void SetMenuOwnWnd(HWND hWnd); 23 24 protected: 25 virtual void OnLButtonDblclk(); 26 virtual void OnRButtonUp(); 27 28 bool ShowMenu(); 29 void DestroyMenu(); 30 31 protected: 32 HWND m_hOwnWnd; 33 HMENU m_hMenu; 34 }; 35 36 #endif nbsp; 从上可知,很显然,托盘消息事件处理器接口ITrayMsgHandler为5种鼠标事件提供了空实现,而CTrayMsgDefaultHandler默认处理器实现了鼠标左键双击(显示m_hOwnWnd指定的窗口)和右键单击的行为(弹出上下文菜单),因此这个类实现了特点1,在使用时要先调用SetMenu方法来设置菜单ID或名称以创建菜单,调用SetMenuOwnWnd方法来设置菜单拥有者窗口。如果需要特定的行为,那么依上文特点2所述,从CBasicTrayIcon或者ITrayMsgHandler派生子类实现那5种(或其中几种)事件即可,对于后种方法,再调用CBasicTrayIcon::SetMsgHandler方法设置为新的特定消息事件处理器。
最后,列出CBasicTrayIcon的源码,如下所示
1 #include "stdafx.h" 2 #include "basic_trayicon.h" 3 using namespace std; 4 5 HWND CBasicTrayIcon::s_hNotifyWnd = NULL; 6 UINT CBasicTrayIcon::s_uIDCount = 0; 7 map<UINT,CBasicTrayIcon*> CBasicTrayIcon::s_trayicons; 8 9 const UINT WM_TRAY_NOTIFY_MSG = ::RegisterWindowMessage(_T("TrayNotifyMsg")); 10 11 bool CBasicTrayIcon::RegisterNotifyWndClass() 12 { 13 WNDCLASSEX wndclass; 14 15 wndclass.cbSize = sizeof(wndclass); 16 wndclass.style = CS_HREDRAW|CS_VREDRAW; 17 wndclass.lpfnWndProc = NotifyWndProc; 18 wndclass.cbClsExtra = 0; 19 wndclass.cbWndExtra = 0; 20 wndclass.hInstance = ::GetModuleHandle(NULL); 21 wndclass.hIcon = 0; 22 wndclass.hCursor = 0; 23 wndclass.hbrBackground = 0; 24 wndclass.lpszMenuName = NULL; 25 wndclass.lpszClassName = _T("NotifyWndClass"); 26 wndclass.hIconSm = NULL; 27 28 return 0!=::RegisterClassEx(&wndclass); 29 } 30 31 bool CBasicTrayIcon::CreateNotifyWindow() 32 { 33 if (s_hNotifyWnd) return true; 34 35 if (!RegisterNotifyWndClass()) 36 return false; 37 38 s_hNotifyWnd = ::CreateWindowEx( 39 0, 40 _T("NotifyWndClass"), 41 _T(""), 42 WS_OVERLAPPEDWINDOW, 43 CW_USEDEFAULT, 44 CW_USEDEFAULT, 45 CW_USEDEFAULT, 46 CW_USEDEFAULT, 47 NULL, 48 NULL, 49 ::GetModuleHandle(NULL), 50 NULL) ; 51 return NULL!=s_hNotifyWnd; 52 } 53 54 CBasicTrayIcon* CBasicTrayIcon::FromID(UINT uID) 55 { 56 map<UINT,CBasicTrayIcon*>::iterator iter = s_trayicons.find(uID); 57 if (iter==s_trayicons.end()) 58 return NULL; 59 return iter->second; 60 } 61 62 void CBasicTrayIcon::OnNotify(UINT uEvent) 63 { 64 switch(uEvent) 65 { 66 case WM_LBUTTONDBLCLK: m_pMsgHandler->OnLButtonDblclk(); break; 67 case WM_LBUTTONDOWN: m_pMsgHandler->OnLButtonDown(); break; 68 case WM_RBUTTONDOWN: m_pMsgHandler->OnRButtonDown(); break; 69 case WM_RBUTTONUP: m_pMsgHandler->OnRButtonUp(); break; 70 case WM_MOUSEMOVE: m_pMsgHandler->OnMouseMove(); break; 71 } 72 } 73 74 LRESULT CALLBACK CBasicTrayIcon::NotifyWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 75 { 76 if (uMsg==WM_TRAY_NOTIFY_MSG) 77 { 78 CBasicTrayIcon* p = CBasicTrayIcon::FromID(wParam); 79 if (p) p->OnNotify(lParam); 80 return 0; 81 } 82 return ::DefWindowProc(hWnd, uMsg, wParam, lParam); 83 } 84 85 CBasicTrayIcon::CBasicTrayIcon() 86 :m_pMsgHandler(this) 87 { 88 m_tnd.uID = ++s_uIDCount; 89 s_trayicons.insert(make_pair(m_tnd.uID,this)); 90 } 91 92 CBasicTrayIcon::~CBasicTrayIcon() 93 { 94 s_trayicons.erase(m_tnd.uID); 95 Destroy(); 96 } 97 98 bool CBasicTrayIcon::Create(HWND hNotifyWnd) 99 { 100 if (NULL==hNotifyWnd) 101 { 102 if (!CreateNotifyWindow()) 103 return false; 104 hNotifyWnd = s_hNotifyWnd; 105 } 106 m_tnd.cbSize = sizeof(NOTIFYICONDATA); 107 m_tnd.hWnd = hNotifyWnd; 108 m_tnd.uCallbackMessage = WM_TRAY_NOTIFY_MSG; 109 m_tnd.uFlags = NIF_MESSAGE; 110 return Shell_NotifyIcon(NIM_ADD, &m_tnd); 111 } 112 113 void CBasicTrayIcon::Destroy() 114 { 115 Shell_NotifyIcon(NIM_DELETE, &m_tnd); 116 } 117 118 bool CBasicTrayIcon::SetNotifyWnd(HWND hWnd) 119 { 120 if (NULL==hWnd) 121 { 122 if (!CreateNotifyWindow()) 123 return false; 124 hWnd = s_hNotifyWnd; 125 } 126 m_tnd.hWnd = hWnd; 127 m_tnd.uCallbackMessage = WM_TRAY_NOTIFY_MSG; 128 m_tnd.uFlags |= NIF_MESSAGE; 129 return Shell_NotifyIcon(NIM_MODIFY, &m_tnd); 130 } 131 132 void CBasicTrayIcon::SetMsgHandler(ITrayMsgHandler* pMsgHandler) 133 { 134 m_pMsgHandler = (pMsgHandler pMsgHandler : this); 135 } 136 137 bool CBasicTrayIcon::SetIcon(HICON hIcon) 138 { 139 m_tnd.uFlags |= NIF_ICON; 140 m_tnd.hIcon = hIcon; 141 return Shell_NotifyIcon(NIM_MODIFY, &m_tnd); 142 } 143 144 bool CBasicTrayIcon::SetIcon(LPCTSTR lpIconName) 145 { 146 HICON hIcon = ::LoadIcon(::GetModuleHandle(NULL),lpIconName); 147 return SetIcon(hIcon); 148 } 149 150 bool CBasicTrayIcon::SetIcon(UINT uID) 151 { 152 HICON hIcon = ::LoadIcon(::GetModuleHandle(NULL),MAKEINTRESOURCE(uID)); 153 return SetIcon(hIcon); 154 } 155 156 bool CBasicTrayIcon::SetTooltipText(LPCTSTR lpText) 157 { 158 m_tnd.uFlags |= NIF_TIP; 159 size_t min = std::min<size_t>(sizeof(m_tnd.szTip)/sizeof(TCHAR)-1,_tcslen(lpText)); 160 _tcsncpy(m_tnd.szTip,lpText,min); 161 m_tnd.szTip[min] = _T('\0'); 162 return Shell_NotifyIcon(NIM_MODIFY, &m_tnd); 163 } 164 165 bool CBasicTrayIcon::SetTooltipText(UINT uID) 166 { 167 TCHAR szBuf[sizeof(m_tnd.szTip)/sizeof(TCHAR)]; 168 ::LoadString(GetModuleHandle(NULL),uID,szBuf,sizeof(szBuf)/sizeof(szBuf)); 169 return SetTooltipText(szBuf); 170 } 171 172 UINT CBasicTrayIcon::GetID() const 173 { 174 return m_tnd.uID; 175 } 176 177 HICON CBasicTrayIcon::GetIcon() const 178 { 179 assert(m_tnd.uFlags&NIF_ICON); 180 return m_tnd.hIcon; 181 } 182 183 LPCTSTR CBasicTrayIcon::GetTooltipText() const 184 { 185 assert(m_tnd.uFlags&NIF_TIP); 186 return m_tnd.szTip; 187 } 188 189 void CBasicTrayIcon::Show() 190 { 191 DWORD dwFlag; 192 #if (_WIN32_IE >= 0x0500) 193 m_tnd.uFlags |= NIF_STATE; 194 m_tnd.dwState = 0; 195 m_tnd.dwStateMask = NIS_HIDDEN; 196 dwFlag = NIM_MODIFY; 197 #else 198 dwFlag = NIM_ADD; 199 #endif 200 Shell_NotifyIcon(dwFlag,&m_tnd); 201 } 202 203 void CBasicTrayIcon::Hide() 204 { 205 DWORD dwFlag; 206 #if (_WIN32_IE >= 0x0500) 207 m_tnd.uFlags |= NIF_STATE; 208 m_tnd.dwState = NIS_HIDDEN; 209 m_tnd.dwStateMask = NIS_HIDDEN; 210 dwFlag = NIM_MODIFY; 211 #else 212 dwFlag = NIM_DELETE; 213 #endif 214 Shell_NotifyIcon(dwFlag,&m_tnd); 215 } CTrayMsgDefaultHandler的源码,如下所示
1 #include "stdafx.h" 2 #include "trayicon_msg_handler.h" 3 4 CTrayMsgDefaultHandler::CTrayMsgDefaultHandler() 5 :m_hOwnWnd(NULL) 6 ,m_hMenu(NULL) 7 { 8 } 9 10 CTrayMsgDefaultHandler::~CTrayMsgDefaultHandler() 11 { 12 DestroyMenu(); 13 } 14 15 bool CTrayMsgDefaultHandler::SetMenu(UINT uID) 16 { 17 return SetMenu(MAKEINTRESOURCE(uID)); 18 } 19 20 bool CTrayMsgDefaultHandler::SetMenu(LPCTSTR lpName) 21 { 22 return m_hMenu=LoadMenu(GetModuleHandle(NULL),lpName); 23 } 24 25 void CTrayMsgDefaultHandler::SetMenuOwnWnd(HWND hWnd) 26 { 27 m_hOwnWnd = hWnd; 28 } 29 30 bool CTrayMsgDefaultHandler::ShowMenu() 31 { 32 assert(m_hMenu); 33 HMENU hSubMenu = GetSubMenu(m_hMenu,0); 34 if (NULL==hSubMenu) return false; 35 36 POINT pos; 37 GetCursorPos(&pos); 38 return ::TrackPopupMenu(hSubMenu, TPM_LEFTALIGN|TPM_BOTTOMALIGN, pos.x, pos.y,0,m_hOwnWnd,NULL); 39 } 40 41 void CTrayMsgDefaultHandler::DestroyMenu() 42 { 43 if (m_hMenu) ::DestroyMenu(m_hMenu); 44 } 45 46 void CTrayMsgDefaultHandler::OnLButtonDblclk() 47 { 48 if (m_hOwnWnd&&::IsWindow(m_hOwnWnd)) 49 ::ShowWindow(m_hOwnWnd,SW_RESTORE); 50 } 51 52 void CTrayMsgDefaultHandler::OnRButtonUp() 53 { 54 ShowMenu(); 55 } |