所谓系统托盘,就是指任务栏最右端显示系统时间和启动程序图标的那一块凹陷矩形区域。将程序最小化到系统托盘,其实就是在系统托盘区为应用程序创建一个运行时图标,当点击程序的最小化按钮时调用ShowWindow(WS_HIDE)将主窗口隐藏不见。还原程序窗口就是当系统托盘图标响应鼠标消息时再调用ShowWindow(WS_SHOW)将主窗口重新显示出来。为应用程序创建系统托盘图标一般是在窗口类的初始化函数中调用Shell_NotifyIcon函数来实现。以MFC的基于对话框的程序为例,现在我们来创建系统托盘图标:
第一步,创建一个基于对话框的MFC应用程序,此处工程名取为Notify
第二部,在CNotifyDlg类的OnInitDialog()函数中先写上Shell_NotifyIcon()函数的空壳
第三步,查阅MSDN,看看Shell_NotifyIcon函数需要的参数信息。我们查到,它需要两个参数,第一个是DWORD 类型的变量dwMessage,它用来指定Shell_NotifyIcon函数的动作,常用的值有如下三个:
NIM_ADD——用于向系统托盘添加图标
NIM_DELETE——用于从系统托盘删除图标
NIM_MODIFY——用于修改系统托盘图标
本例我们需要向系统托盘添加图标,所以使用NIM_ADD作为Shell_NotifyIcon函数的第一个参数。
接下来我们看Shell_NotifyIcon函数的第二个参数。该参数是一个PNOTIFYICONDATA类型的变量lpdata。PNOTIFYICONDATA是个什么类型呢?查阅MSDN很容易发现,原来它是一个指向NOTIFYICONDATA结构体的指针。接下来我们在MSDN中查看关于NOTIFYICONDATA的说明。你会发现,哇,好丰满啊!的确,这个结构体中的元素太多了,但是,千万不要被他吓唬住,就仅仅创建系统托盘图标而言,我们只需要用到其中一部分。它们是:
DWORD cbSize——用来保存该结构体的大小,用sizeof(NOTIFYICONDATA)赋值
HWND hWnd——托盘图标所属应用程序窗口句柄,用m_hWnd或this赋值
UINT uID——托盘所用图标资源的ID,直接用图标资源ID赋值
UINT uFlags——指示托盘图标包含的内容,赋值方法参见示例代码
UINT uCallbackMessage——指定托盘图标的事件消息,用自定义消息赋值
HICON hIcon——托盘图标句柄,用LoadIcon函数获取图标句柄
TCHAR szTip[64]——指定当光标停留在托盘图标上时显示的提示文本信息
好了,现在到了创建NOTIFYICONDATA结构体对象的时候了。在Shell_NotifyIcon函数之前创建NOTIFYICONDATA结构体对象并对其赋值,然后填充Shell_NotifyIcon函数的参数。代码如下:
NOTIFYICONDATA NotiData;
NotiData.cbSize=sizeof(NOTIFYICONDATA);
NotiData.hWnd=m_hWnd;
NotiData.uID=IDR_MAINFRAME;
NotiData.hIcon=LoadIcon(theApp.m_hInstance,MAKEINTRESOURCE(IDR_MAINFRAME));
NotiData.uCallbackMessage=USER_NIF_MESSAGE; //自定义消息
NotiData.uFlags=NIF_ICON | NIF_MESSAGE | NIF_TIP;
lstrcpy(NotiData.szTip,L"This is simple program"); //注意这里的lstrcpy函数
Shell_NotifyIcon(NIM_ADD,&NotiData);
第四步,定制托盘图标事件消息。具体操作步骤如下:
1. 在NotifyDlg.h中添加语句:#define USER_NIF_MESSAGE WM_USER+1
2. 在NotifyDlg.h中的消息响应函数原型声明部分(DECLARE_MESSAGE_MAP宏之前)添加:
afx_msg LRESULT OnNotifyIcon(WPARAM wParam,LPARAM lParam);
3. 在NotifyDlg.cpp中的消息映射表(BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间)中添加:ON_MESSAGE(USER_NIF_MESSAGE,OnNotifyIcon)
4. 在NotifyDlg.cpp中构建自定义消息响应函数OnNotifyIcon的框架:
LRESULT CNotifyDlg::OnNotifyIcon(WPARAM wParam, LPARAM lParam)
{
return 0;
}
现在编译运行一下我们的程序,系统托盘图标似乎已经创建好了。但是,到现在为止,我们所做的工作仅仅是使程序在运行时有了一个托盘图标而已,它还无法最小化到系统托盘。下面,我们来实现它最小化到系统托盘的功能:
第五步,在视图选项卡上切换到资源视图,将对话框属性中的Minimize box项设置为TRUE,这时候就有了一个最小化按钮了。然而,运行程序点击最小化按钮,我们发现程序并没有最小化到系统托盘,它在任务栏还有个图标,这显然不是我们想要的。因此,我们还要做点修改:
1. 切换回类视图,选择CNotifyDlg, 在属性的消息中为WM_SYSCOMMAND消息添加消息处理函数。
2. 在OnSysCommon函数中做如下修改:
void CNotifyDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
// TODO: Add your message handler code here and/or call default
switch(nID)
{
case SC_MINIMIZE:
ShowWindow(SW_HIDE);
break;
case SC_CLOSE:
PostMessage(WM_QUIT);
break;
default:
CDialog::OnSysCommand(nID, lParam);
}
}
现在运行程序,已经可以最小到系统托盘了。下面我们对托盘图标做一下处理,来完善这个程序。
第六步,处理托盘图标:
LRESULT CNotifyDlg::OnNotifyIcon(WPARAM wParam, LPARAM lParam)
{
if(wParam=!IDR_MAINFRAME)
return 1;
if(lParam==WM_RBUTTONUP)
{
POINT point; //右键弹出菜单弹出坐标点
GetCursorPos(&point); //获取当前光标坐标点
CMenu RMenu; //创建菜单对象
RMenu.CreatePopupMenu(); //设置菜单为弹出菜单
RMenu.AppendMenuW(MF_STRING,WM_DESTROY,L"退出程序");//添加菜单项
RMenu.TrackPopupMenu(TPM_LEFTALIGN,point.x,point.y,this);//显示菜单
RMenu.Detach();
RMenu.DestroyMenu();
}
if(lParam==WM_LBUTTONDOWN)
{
ShowWindow(SW_SHOW);//显示主程序窗口
}
return 0;
}
这段代码我就不详细解释了,大家弄明白OnNotifyIcon函数的wParam参数是托盘图标的ID,lParam是托盘图标接收到的事件消息这段代码就不难理解了。