原文地址:《VC托盘编程》
以VC编程中,经常涉及到托盘操作,比如为我们的程序添加托盘图标,添加托盘左键响应或右键菜单,我们常用的QQ就是个托盘程序。
好了下面我们来看下VC中托盘编程要注意些什么。首先我们要弄清楚一个结构体-NOTIFYICONDATA,这个结构体是托盘编程的关键。
typedef struct _NOTIFYICONDATA {
DWORD cbSize;
HWND hWnd;
UINT uID;
UINT uFlags;
UINT uCallbackMessage;
HICON hIcon;
TCHAR szTip[64];
DWORD dwState;
DWORD dwStateMask;
TCHAR szInfo[256];
union {
UINT uTimeout;
UINT uVersion;
};
TCHAR szInfoTitle[64];
DWORD dwInfoFlags;
GUID guidItem;
} NOTIFYICONDATA, *PNOTIFYICONDATA;
我们来看一下各成员变量是代表什么,其实一般应用当中注意前面十来个成员就行了,下面那几个成员用得比较少。
cbSize 结构体的大小,以字节为单位。
hWnd 窗口的句柄。标示的窗口用来接收与托盘图标相关的消息。Shell_NotifyIcon函数调用时,hWnd和uID成员用来标示具体要操作的图标。
uID 应用程序定义的任务栏图标的标识符。Shell_NotifyIcon函数调用时,hWnd和uID成员用来标示具体要操作的图标。通过将多次调用,你可以使用不同的uID将多个图标关联到一个窗口hWnd。
uFlags 此成员表明具体哪些其他成员为合法数据(即哪些成员起作用)。此成员可以为以下值的组合:
NIF_ICON hIcon成员起作用。
NIF_MESSAGE uCallbackMessage成员起作用。
NIF_TIP szTip成员起作用。
NIF_STATE dwState和dwStateMask成员起作用。
NIF_INFO 使用气球提示代替普通的工具提示框。szInfo, uTimeout, szInfoTitle和dwInfoFlags成员起作用。
NIF_GUID 保留。
uCallbackMessage
应用程序定义的消息标示。当托盘图标区域发生鼠标事件或者使用键盘选择或激活图标时,系统将使用此标示向由hWnd成员标示的窗口发送消息。消息响应函数的wParam参数标示了消息事件发生的任务栏图标,lParam参数根据事件的不同,包含了鼠标或键盘的具体消息,例如当鼠标指针移过托盘图标时,lParam将为WM_MOUSEMOVE。
hIcon 增加、修改或删除的图标的句柄。注意,windows不同版本对于图标有不同要求。Windows XP可支持32位。
szTip 指向一个以\0结束的字符串的指针。字符串的内容为标准工具提示的信息。包含最后的\0字符,szTip最多含有64个字符。对于Version 5.0 和以后版本,szTip最多含有128个字符(包含最后的\0字符)。
dwState Version 5.0,图标的状态,有两个可选值,如下:
NIS_HIDDEN 图标隐藏
NIS_SHAREDICON 图标共享
dwStateMask Version 5.0. 指明dwState成员的那些位可以被设置或者访问。比如设置此成员为NIS_HIDDEN,将导致只有hidden状态可以被获取。
szInfo Version 5.0. 指向一个以\0结束的字符串的指针。字符串的内容为气球提示内容。最多含有255个字符。如果要移除已经存在的气球提示信息,设置uFlags成员为NIF_INFO,同时将szInfo设为空。
uTimeout 和uVersion成员为联合体。uTimeout表示气球提示超时的时间,单位为毫秒,此时间后气球提示将消失。系统默认气球提示的超时时间最小值为10秒,最大值为30秒。如果设置的uTimeout的值小于10将设置最小值,如果大于30将设置最大值。将超时时间分为最大最小两种,是因为解决不同图标的气球提示同时弹出的问题,详细内容请参考MSDN中NOTIFYICONDATA结构体说明的remarks。
uVersion Version 5.0. 和uTimeout成员为联合体。用来设置使用Windows 95 还是 Windows 2000风格的图标消息接口。请参考Shell_NotifyIcon函数的说明获取更多信息。只有当使用Shell_NotifyIcon函数发送NIM_SETVERSION消息时,此成员才有作用。可选的值如下:
0 使用Windows 95风格。针对Windows 2000版本之前的windows设计的软件请使用此值。
NOTIFYICON_VERSION 使用Windows 2000风格。 针对Windows 2000版本以及以后版本的windows设计的软件请使用此值。
szInfoTitle Version 5.0. 指向一个以\0结束的字符串的指针。字符串的内容为气球提示的标题。此标题出现在气球提示框的上部,最多含有63个字符。
dwInfoFlags Version 5.0. 设置此成员用来给气球提示框增加一个图标。增加的图标出现在气球提示标题的左侧,注意如果szInfoTitle成员设为空字符串,则图标也不会显示。可选值如下:
NIIF_ERROR 错误图标。
NIIF_INFO 信息图标。
NIIF_NONE 没有图标。
NIIF_USER 使用用户使用hIcon成员指明的图标,要求Windows XP Service Pack 2 (SP2)或以后系统。
NIIF_WARNING 警告图标。
NIIF_ICON_MASK Version 6.0. 保留。
NIIF_NOSOUND Version 6.0. 禁止播放相应声音。
guidItem Version 6.0. 保留。
成员比较多,所以说明也挺长,如我上面所说的,搞清楚前面十来个成员代表的含意就行了,后面的不常用。好了,我们看下托盘编程的一般步骤,首先在我们的测试工程(MFC 应用程序)里的对话框内声明一个全局NOTIFYICONDATA结构体变量:
NOTIFYICONDATA m_SkyNID;
然后在OnInitDialog函数中初始化:
m_SkyNID.cbSize = (DWORD)sizeof(NOTIFYICONDATA);
m_SkyNID.hWnd = this->m_hWnd;
m_SkyNID.uID = IDR_SKYNID_MENU;
m_SkyNID.uFlags = NIF_ICON|NIF_MESSAGE|NIF_TIP ;
m_SkyNID.dwState = NIS_SHAREDICON;
m_SkyNID.uCallbackMessage = WM_TASKMANAGE;
m_SkyNID.hIcon = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME));
_tcscpy(m_SkyNID.szTip, _T("系统状态-未登录"));
Shell_NotifyIcon(NIM_ADD, &m_SkyNID);
***************************上面的代码在VC6.0下面编译有错误,可以参照《VC6.0托盘编程》上面有解决之道****************************
搞清楚结构体各成员所代表的含意,上面的初始化代码应该不难看懂,m_SkyNID.uID我设置为菜单资源的ID,其实这个可以自定义,只要唯一就行了。最后一句用windows API Shell_NotifyIcon将初始化的结构体变量所代表的托盘图标添加到系统托盘中,这样你的桌面右下解就会出现你的应用程序托盘图标。当然,光这样在程序退出的时候这个图标不会自动删除,要在程序退出时添加处理代码,我是这样做的,在OnDestroy函数中添加代码:
Shell_NotifyIcon(NIM_DELETE, &m_SkyNID);
至此你的程序能在启动和退出的时候自动在系统托盘中创建和删除图标,接下来我们为这个托盘添加右键菜单。在资源中添加菜单,我这里菜单资源ID是IDR_SKYNID_MENU。
由于在初始化代码中m_SkyNID.uCallbackMessage = WM_TASKMANAGE设置了托盘图标区域发生鼠标事件或者使用键盘选择或激活图标时,hWnd标示的窗口会收到WM_TASKMANAGE消息(这是个自定义消息#define WM_TASKMANAGE WM_USER+100),下面我们就在对应窗口中响应这个消息,在消息映射宏中添加消息映射
ON_MESSAGE(WM_TASKMANAGE,OnTaskManage)
并添加消息响应函数OnTaskManage
LRESULT CSkyTestDlg::OnTaskManage(WPARAM wParam, LPARAM lParam)
{
if(wParam != IDR_SKYNID_MENU)
return 1;
switch(lParam)
{
case WM_RBUTTONUP:
{
CMenu * pTaskMenu, taskMenu;
CPoint pMousePosition;
taskMenu.LoadMenu(IDR_SKYNID_MENU);
pTaskMenu = taskMenu.GetSubMenu(0);
/******为了简单测试,也可以把pTaskMenu加载成系统菜单pTaskMenu=GetSystemMenu(false)**********/
GetCursorPos(&pMousePosition);
SetForegroundWindow();
pTaskMenu->TrackPopupMenu(TPM_RIGHTALIGN,pMousePosition.x,pMousePosition.y,this);
break;
}
case WM_LBUTTONDOWN:
{
break;
}
}
return 0;
}
/************************
上面代码涉及到的IDR_SKYNID_MENU菜单,可以如下面实行编辑:
记事本打开资源文件 **.rc,找到里面的代表菜单资源的地方编辑菜单如下:
IDR_SKYNID_MENU MENU DISCARDABLE
BEGIN
POPUP "MyMenu"
BEGIN
MENUITEM "关于", ID_ABOUT
MENUITEM "还原", ID_RESET
MENUITEM "退出", ID_EXIT
END
END
也是就一个弹出式菜单
*******************************/
简单对上面代码的作一下解释先定义CMenu变量taskMenu并LoadMenu资源ID为IDR_SKYNID_MENU的菜单,再用CMenu指针变量得到加载的菜单指针,并利用TrackPopupMenu函数将菜单显示到指定的坐标。SetForegroundWindow()使用这个函数是为了将菜单设为前端窗口,解决鼠标离开菜单单击或双击菜单不会自动消失的问题。最后,对每个右键菜单项添加事件响应处理代码,右键菜单项->Add Event Handler系统自动添加三处代码,这个就不赘述了,以上面菜单的退出程序子项为例,看代码
afx_msg void OnSkyTestExit();// 第一处 头文件的响应函数声明
ON_COMMAND(ID_SKYTEST_EXIT, &CSkyTestDlg::OnSkyTestExit) //第二处 cpp文件消息映射宏
void CSkyTestDlg::OnSkyTestExit() // 第三处 cpp文件 消息响应函数
{
if(IsWindow(m_SkyLoginDlg.m_hWnd))
{
m_SkyLoginDlg.SendMessage(WM_CLOSE);
}
EndDialog(TRUE);
}
至此我们的托盘右键菜单已经能正常工作了,文章比较简单,欢迎各位跟贴交流。
***************************************************************************************************************************************************
下面给出用到的函数说明!
***************************************************************************************************************************************************
一、Shell_NotifyIcon 函数说明 函数格式
BOOL Shell_NotifyIcon( DWORD dwMessage,PNOTIFYICONDATA lpdata);***************************************************************************************************************************************************
再来一份简短的介绍,步骤一样,熟能生巧!
***************************************************************************************************************************************************
原文地址:《VC++MFC对话框应用程序实现程序最小化到系统托盘》
第一步:定义一个 NOTIFYICONDATA 结构变量 nid。NOTIFYICONDATA结构体定义可以在MSDN中查找,就不做多做介绍。
第二步:设置NOTIFYICONDATA变量属性:
nid.cbSize =sizeof(NOTIFYICONDATA);
nid.hWnd =m_hWnd;
nid.uID = 0;
nid.hIcon =m_hIcon;
nid.uFlags = NIF_ICON|NIF_MESSAGE|NIF_TIP|NIF_INFO;
nid.uCallbackMessage =WM_NOTIFYICON;//#define WM_NOTIFYICON (WM_USER +101) 自己定义
wcscpy(nid.szTip, L"这是标签");
wcscpy(nid.szInfo, L"这是气泡提示消息");
wcscpy(nid.szInfoTitle, L"这是气泡提示标题");
nid.dwInfoFlags = NIIF_INFO;
nid.uTimeout = 1000;
第三步:将图标显示到系统托盘,只需调用一个API即可完成.
Shell_NotifyIcon (NIM_ADD,&nid);
第四步:图标添加完成了,我们通常需要让它响应用户的各种操作。如双击显示窗体,右键弹出上下文菜单等.
首先,我们定义一个回调函数
LRESULT OnNotifyIcon(WPARAM wParam, LPARAM lParam)
{
switch(lParam)//根据lParam判断相对应的事件
{
case WM_LBUTTONDBLCLK://如果左键双击托盘图标,则显示窗体
ShowWindow (SW_SHOWNORMAL );
SetForegroundWindow();
break;
case WM_RBUTTONUP://如果右键菜单弹起,则弹出菜单
CPoint pos;
GetCursorPos(&pos);
if(pMenuContext != NULL)
{
SetForegroundWindow();//加这句是为了鼠标点击其他地方时,弹出的菜单能够消失
pMenuContext->TrackPopupMenu(TPM_RIGHTBUTTON|TPM_RIGHTALIGN,pos.x+1,pos.y+1,this);
}
break;
}
return 0;
}
然后在BEGIN_MESSAGE_MAP 与END_MESSAGE_MAP 之间添加
ON_MESSAGE (WM_NOTIFYICON ,&OnNotifyIcon)
进行消息映射就完成了
我们已经完成了对程序添加托盘图标,而且可以通过图标对应用程序进行一定的操作,但是当程序退出的时候,系统托盘图标并不会自动删除,所以我们还需要在 OnClose() 中调用::Shell_NotifyIcon (NIM_DELETE ,&nid);删除托盘图标.