1)對于托盤通知消息的接收處理,提供了一個(gè)默認(rèn)常用的實(shí)現(xiàn),也就是通知消息接收由托盤類對象本身接收,消息事件的處理也由托盤對象本身處理,雙擊圖標(biāo)顯示某個(gè)窗口,右鍵單擊圖標(biāo)顯示快捷菜單。
2)支持事件處理擴(kuò)展,有兩種方法,一是提供一個(gè)接口設(shè)置消息事件處理器,從而將處理邏輯轉(zhuǎn)到其它對象,這個(gè)對象要支持消息事件處理器接口約定;二是提供消息事件處理的虛函數(shù)回調(diào),可由派生類重寫定制。
3)托盤圖標(biāo)ID的管理,ID為UINT類型整數(shù),一個(gè)對象對應(yīng)一個(gè)ID,每當(dāng)對象創(chuàng)建時(shí),分配一個(gè),從1開始,為防止沖突造成托盤圖標(biāo)創(chuàng)建失敗,當(dāng)對象銷毀時(shí),并不遞減ID計(jì)數(shù),因此ID一直是遞增的,這也是最簡單的方案。
4)支持內(nèi)部或外部窗口來接收通知消息,這兩種模式可以隨時(shí)相互轉(zhuǎn)換,一個(gè)托盤對象只對應(yīng)一個(gè)窗口,多個(gè)托盤對象可以對應(yīng)同一個(gè)窗口。如果是內(nèi)部窗口,那么內(nèi)部窗口是全局的有且僅有一個(gè),由其窗口過程根據(jù)托盤ID將消息分發(fā)到對應(yīng)的托盤對象。如果是外部窗口,那么可以由外部窗口自己處理,也可以調(diào)用托盤類的方法轉(zhuǎn)到其它對象處理。
下面來看下基本托盤圖標(biāo)類的接口定義:
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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

1)Create---創(chuàng)建托盤圖標(biāo),請注意這里只是創(chuàng)建,沒有關(guān)聯(lián)圖標(biāo),因此在任務(wù)欄上并不會顯示,要顯示需要接著調(diào)用SetIcon系列方法,參數(shù)hWnd為托盤通知消息接收窗口,默認(rèn)為NULL,當(dāng)為NULL的時(shí)候,則會創(chuàng)建一個(gè)內(nèi)部隱藏的窗口來接收消息,否則由其指定的外部窗口來接收托盤通知消息。如果成功那么返回true,否則返回false。
2)Destroy---銷毀托盤圖標(biāo)。
3)SetNotifyWnd---設(shè)置通知消息接收窗口,當(dāng)參數(shù)hWnd為NULL時(shí),則轉(zhuǎn)移到內(nèi)部窗口接收通知消息,否則轉(zhuǎn)為其指定的外部窗口接收通知消息。因此,這個(gè)方法實(shí)現(xiàn)了特點(diǎn)4。
4)SetMsgHandler---設(shè)置托盤通知消息處理器,參數(shù)pMsgHandler為ITrayMsgHandler指針類型,當(dāng)其為NULL時(shí),則處理為其指定的消息事件處理器,否則為托盤對象自己。因此,這個(gè)方法實(shí)現(xiàn)了特點(diǎn)2。
5)SetIcon---設(shè)置托盤圖標(biāo),有3個(gè)重載形式,如果成功那么返回true,否則返回false。
6)SetToolipText---設(shè)置提示文本,有2個(gè)重載形式,如果成功返回true,否則返回false。
7)Show---顯示托盤圖標(biāo),具體的內(nèi)部實(shí)現(xiàn)根據(jù)_WIN_IE的版本而定,如果成功返回true,否則返回fasle。
8)Hide---隱藏托盤圖標(biāo),具體的內(nèi)部實(shí)現(xiàn)根據(jù)_WIN_IE的版本而定,如果成功返回true,否則返回fasle。
9)FromID---這是個(gè)靜態(tài)方法,從ID轉(zhuǎn)換為托盤對象,如果成功那么返回對應(yīng)的指針,否則返回NULL。
10)OnNotify---通知消息事件分發(fā)處理。公開方法9和10,是為了支持由外部窗口接收通知消息時(shí)可以轉(zhuǎn)交給其它對象的靈活性(見特點(diǎn)4)。這個(gè)類默認(rèn)實(shí)現(xiàn)消息的處理者為托盤對象自己,即m_pMsgHandler在構(gòu)造時(shí)指向它自己。
接下來,看下消息事件處理器的接口定義:
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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

最后,列出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
}

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

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
}

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55
