一、需求

本文实现程序系统托盘。

" />

MFC小笔记:系统托盘实现

一、需求

本文实现程序系统托盘。

二、界面

主界面为对话框,有最小化、最大化、关闭等功能。

三、原理

利用系统托盘类NOTIFYICONDATA,响应自定义消息,处理托盘鼠标事件。

四、编码

4.1 消息定义

定义消息ID,必须大于WM_USER,为方便起见,可在stdafx.h中定义:

1
#define WM_SHOWTASK (WM_USER+1)  // 系统托盘事件

在子对话框头文件声明消息响应函数:

1
2
3
afx_msg LRESULT OnSystemtray(WPARAM wParam, LPARAM lParam);
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnDestroy();

在子对话框实现文件,添加消息与响应函数的关联:

1
2
3
4
5
6
7
8
9
BEGIN_MESSAGE_MAP(CMyTestDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
// ...
ON_MESSAGE(WM_SHOWTASK, OnSystemtray) // 托盘
ON_WM_SIZE()
ON_WM_DESTROY() // 销毁
END_MESSAGE_MAP()

4.2 变量声明

在类中声明

1
2
3
private: 
NOTIFYICONDATA m_nid;
BOOL m_fMainWinShow; // 托盘 双击左键显示/隐藏窗口

4.3 初始化

在OnInitDialog函数中初始化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 系统托盘
m_nid.cbSize = (DWORD)sizeof(NOTIFYICONDATA);
m_nid.hWnd = this->m_hWnd;
m_nid.uID = IDR_MAINFRAME;
m_nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO;
m_nid.uCallbackMessage = WM_SHOWTASK; // 自定义的消息名称
m_nid.hIcon = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME));
wcscpy_s(m_nid.szTip, L"主程序"); // 提示程序名称
Shell_NotifyIcon(NIM_ADD, &m_nid); // 在托盘区添加图标

// 如此实现,任务栏没有图标,但窗口没有最小化按钮
#if 0
//ModifyStyleEx(WS_EX_APPWINDOW, WS_EX_TOOLWINDOW); // 不在任务栏中显示
// ShowWindow(SW_MINIMIZE);
#endif
//ModifyStyleEx(WS_EX_APPWINDOW, WS_EX_TOOLWINDOW); // 不在任务栏中显示

// 可以实现,但界面会闪烁
ShowWindow(SW_MINIMIZE); // 不能使用SW_HIDE
PostMessage(WM_SYSCOMMAND, SC_MINIMIZE, 0);

m_fMainWinShow = FALSE;

同一文件,实现WM_SHOWTASK消息的响应,包括鼠标左键、右键的处理:

1
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
LRESULT CMyTestDlg::OnSystemtray(WPARAM wParam, LPARAM lParam)
{
if (wParam != IDR_MAINFRAME)
return 1;
switch (lParam)
{
case WM_RBUTTONUP: // 右键起来时弹出菜单
{
LPPOINT lpoint = new tagPOINT;
GetCursorPos(lpoint); // 得到鼠标位置
CMenu menu;
menu.CreatePopupMenu(); // 声明一个弹出式菜单
menu.AppendMenu(MF_STRING, WM_DESTROY, L"退出");
SetForegroundWindow();
menu.TrackPopupMenu(TPM_LEFTALIGN, lpoint->x, lpoint->y, this);
HMENU hmenu = menu.Detach();
menu.DestroyMenu();
delete lpoint;
}
break;
case WM_LBUTTONDBLCLK: // 双击左键的处理
{
if (!m_fMainWinShow)
{
ShowWindow(SW_SHOWNORMAL); // 显示主窗口
SetForegroundWindow();
m_fMainWinShow = TRUE;
}
else
{
ShowWindow(SW_HIDE); // 隐藏主窗口
SetForegroundWindow();
m_fMainWinShow = FALSE;
}
}
break;
}
return 0;
}

4.4 响应最小化、窗口销毁事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

void CMyTestDlg::OnSize(UINT nType, int cx, int cy)
{
CDialogEx::OnSize(nType, cx, cy);

if (nType == SIZE_MINIMIZED)
{
ShowWindow(SW_HIDE); // 当最小化时,隐藏主窗口
}
}

void CMyTestDlg::OnDestroy()
{
CDialogEx::OnDestroy();

// 在托盘区删除图标
Shell_NotifyIcon(NIM_DELETE, &m_nid);
}

本文实现的系统托盘,右键只有“退出”一个选项,可酌情添加。
另外,在启动时最小化,窗口会有瞬间闪烁的现象,在可接受范围,故不解决。