看到火哥给他的程序加上了系统托盘,我的没有很是不爽。于是花了几分钟找了些资料,给自己的也加上了系统托盘。现在将我的实现步骤分享一下。
先来解释下什么是系统托盘:其实准确的说法应该是任务栏通知区域。系统托盘区可以看着是任务栏下的一个子工具栏。(任务栏最右面)。一般像QQ,酷狗,杀软,百度云等很多程序都会在系统托盘里显示图标。
我们需要完成的任务:
1.隐藏任务栏的程序图标。
2.系统托盘创建图标
3.托盘图标右键菜单及菜单消息处理函数
4.添加托盘消息处理函数
5.销毁托盘图标
1.隐藏任务栏的程序图标。
这个是最简单的,我们只需要在主窗口的OnInitDialog()函数里加上 ModifyStyleEx(WS_EX_APPWINDOW, WS_EX_TOOLWINDOW);修改主窗口的属性就可以了。
2.系统托盘创建图标
1.添加系统托盘首先要了解一个结构体,_NOTIFYICONDATA ,下面列出了这个结构体的成员,并对我们需要关注的成员加上了注释。
/***该系统需要处理的任务栏状态区的消息***/
typedefstruct_NOTIFYICONDATA{
DWORDcbSize;//结构体的大小
HWNDhWnd;//窗口的句柄
UINTuID;//应用程序定义的任务栏图标的标识符
UINTuFlags;//此成员表明具体哪些其他成员为合法数据
UINTuCallbackMessage;//自定义的消息标识
HICONhIcon;//托盘图标的句柄
TCHARszTip[64];//指向一个以/0结束的字符串的指针,当鼠标指向系统托盘时现实的提示
DWORDdwState;
DWORDdwStateMask;
TCHARszInfo[256];//指向用来表示气泡内容的字符串。
union{
UINTuTimeout;//表示气球提示超时的时间,单位为毫秒,此时间后气球提示将消失
UINTuVersion;//
};
TCHARszInfoTitle[64];//指向用来表示气泡标题的字符串。
DWORDdwInfoFlags;
GUIDguidItem;
HICONhBalloonIcon;
}NOTIFYICONDATA,*PNOTIFYICONDATA;
2.在主窗口的类中(XXXXXDlg.h XXXXX是你的工程名)添加成员变量 NOTIFYICONDATA m_nid;
3. 在XXXXDlg.cpp(XXXXX是你的工程名)文件中加上 #define WM_SYSTEMTRAY WM_USER+100 。其中100的值可以自定义。 这个宏也可以加到其他地方。看自己习惯。
4.然后实现对改成员的初始化函数
VOID CControlCenterDlg::InitNid()//CControlCenterDlg 改成你自己的主窗口名
{
m_nid.cbSize = sizeof(NOTIFYICONDATA);
m_nid.hWnd = m_hWnd;
m_nid.uID = IDR_MAINFRAME;
m_nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP | NIF_INFO;
m_nid.uCallbackMessage = WM_SYSTEMTRAY; //自定义消息
m_nid.hIcon = m_hIcon; //这里的是程序的图标,如果想修改可以添加图标资源, 把 这行代码改成m_nid.hIcon = LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(/* 你添加的图标的ID*/));strcpy_s(m_nid.szTip, "滴水爱达人");
strcpy_s(m_nid.szInfo, _T(""));
strcpy_s(m_nid.szInfoTitle, _T("爱达人官网"));
m_nid.dwInfoFlags = NIIF_INFO;
m_nid.uTimeout = 2000; //这个时间的长短是气泡显示的时间
::Shell_NotifyIcon(NIM_ADD, &m_nid);
然后在该类的OnInitDialog()函数中调用初始化函数InitNid()。这个时候编译运行已经可以在系统托盘里看到图标了。但是我们还没有添加消息处理 和 右键菜单以及程序退出后托盘中的图标不会自动消失
3.托盘图标右键菜单
添加右键菜单的方法有很多,这里我只举例最简单的。
1.在程序的资源视图中添加菜单。(资源文件夹上右键-->添加资源,选择Menu,然后点新建)。如下图所示,可以直接输入菜单的名称,比如我先输入了右键菜单,输入完以后又在他的下面输入了“编程达人”和“退出”两项。
2.选中编程达人,右键-->添加事件处理程序
3.在弹出的对话框里选择要关联的类
4.在生成的代码中,加上自己的代码。我这里实现的功能是显示主窗口
ShowWindow(SW_SHOWNORMAL);
5.同样的方法实现退出项,并加上如下代码
OnCancel();
4.添加托盘消息处理函数
右键菜单加好了,但是我们现在点击系统托盘图标的时候是没有反应的,因为我们还没有添加对应的消息处理函数.这里我提供里两种方法,看个人选择。
第一种
1.在XXXXXDlg类中加上如下函数声明: afx_msg LRESULT onShowTask(WPARAM wParam, LPARAM lParam);
2. 在XXXXXDlg.cpp文件的BEGIN_MESSAGE_MAP 和 END_MESSAGE_MAP()中间加上如下代码 ON_MESSAGE(WM_SYSTEMTRAY, onShowTask)//注意不要有分号
3.实现消息处理函数的代码
switch (lParam)
{
case WM_RBUTTONUP://右键单击 弹出菜单
{
CMenu menu;
menu.LoadMenu(IDR_MENU2);
CMenu *pPopUp = menu.GetSubMenu(0);
CPoint pt;
GetCursorPos(&pt);
SetForegroundWindow();//顶层显示主窗口
pPopUp->TrackPopupMenu(TPM_RIGHTBUTTON, pt.x, pt.y, this);//确定弹出式菜单的位置
HMENU hmenu = menu.Detach();//资源回收
menu.DestroyMenu();
}break;
case WM_LBUTTONUP://左键单击显示主窗口
{
this->ShowWindow(SW_SHOWNORMAL);//简单的显示主窗口完事儿
}break;
}
return 0;
加上代码以后就完成啦,可以弹出右键菜单 和单击图标显示主窗口了。
第二种
我们可以重载WindowProc函数。 高版本编译器中添加重载函数的方法如下:
1.在类视图中选择我们需要重载WindowProc函数的类,右键-->属性。 在属性对话框中点击 重载按钮,然后找到 WindowProc函数,选择添加。
添加代码实现对消息的处理:
LRESULT CMFCApplication3Dlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_SYSTEMTRAY: //自定义消息
if (lParam == WM_LBUTTONUP)
{
ShowWindow(SW_SHOWNORMAL);
break;
}
if (lParam == WM_RBUTTONDOWN)
{
//右击弹出托盘菜单
CMenu menu;
menu.LoadMenu(IDR_MENU1);
CMenu *pPopUp = menu.GetSubMenu(0);
CPoint pt;
GetCursorPos(&pt);
SetForegroundWindow();
pPopUp->TrackPopupMenu(TPM_RIGHTBUTTON, pt.x, pt.y, this);
SetForegroundWindow();//顶层显示主窗口
HMENU hmenu = menu.Detach(); //资源回收
menu.DestroyMenu();
}
}
return CDialogEx::WindowProc(message, wParam, lParam);//这句代码千万不要删除掉哦!
}
5.销毁托盘图标
当我们点击窗口的关闭按钮和右键菜单中的退出的时候,托盘中的图标是不能自动消失的。我们需要做的是重载OnCancel函数,方法和重载WindowProc函数一样,然后在OnCancel函数中加上::Shell_NotifyIcon(NIM_DELETE, &m_nid);这个时候我们退出程序以后,系统托盘中的图标也就消失了。
应该还有其他的方式实现系统托盘,有什么问题和疑问欢迎留言交流。