VBGood网站全文搜索 Google

搜索VBGood全站网页(全文搜索)

VB爱好者乐园(VBGood)

 找回密码
 立即注册
搜索
查看: 11302|回复: 9

[转帖] 关于NOTIFYICONDATA结构体的一些新特性

[复制链接]
 楼主| 发表于 2011-1-20 20:07:24 | 显示全部楼层 |阅读模式
最近初接触MFC,是为了实现一个传感网络的上位机。
在实现托盘提示时,在网上搜索了不少资料,但已经翻译的资料都比较老。
在查看原版的MSDN后,发现NOTIFYICONDATA这个结构中有几项新特性,可以实现比较新的托盘/气泡特性。
例如:气泡操作响应(想想MSN的单击气泡关闭提示?)、隐藏图标|显示图标(不通过删除)、自定义大Balloon Tip图标等,还是比较吸引人的。
但是,实现这些功能的同时,需要一些额外的代码来提高程序的兼容性。



我把自己摸索的一些结果总结出来吧:
在VS2008+MFC+XP和WIN7 64bit下进行测试通过。
先贴下NOTIFYICONDATA结构。


  1. typedef struct _NOTIFYICONDATA {   
  2.     DWORD cbSize;//   
  3.     HWND hWnd;   
  4.     UINT uID;   
  5.     UINT uFlags;//   
  6.     UINT uCallbackMessage;//   
  7.     HICON hIcon;//   
  8.     TCHAR szTip[64];//   
  9.     DWORD dwState;   
  10.     DWORD dwStateMask;   
  11.     TCHAR szInfo[256];   
  12.     union {   
  13.         UINT uTimeout;   
  14.         UINT uVersion;//   
  15.     };   
  16.     TCHAR szInfoTitle[64];   
  17.     DWORD dwInfoFlags;//   
  18.     GUID guidItem;//   
  19.     HICON hBalloonIcon;//   
  20. } NOTIFYICONDATA, *PNOTIFYICONDATA;
复制代码

以下挑出几个不同的地方,一一做说明。

cbSize
网上通常的做法是

  1. nid.cbSize=(DWORD)sizeof(NOTIFYICONDATA);
复制代码

在最新的MSDN中指出:由于NOTIFYICONDATA结构体的大小在不同的Shell32.dll下,是不一样的,若是笼统的使用sizeof(NOTIFYICONDATA)来获取长度,使用的是当前定义的NOTIFYICONDATA结构长度,程序很可能在早期版本的Shell32.dll中无法运行。

在初始化NOTIFYICONDATA结构之前,我们可以使用函数

  1. HRESULT CALLBACK DllGetVersion(DLLVERSIONINFO *pdvi);
复制代码
来获取Shell32.dll的版本,并根据不同版本的Shell32.dll指定cbSize的大小。

DllGetVersion并不是一个API,具体的使用方法,请参看我另一篇博文《正确使用DllGetVersion》。


Shell32.dll VersioncbSize
6.0.6 or higher (Windows Vista and later)sizeof(NOTIFYICONDATA)
6.0 (Windows XP)NOTIFYICONDATA_V3_SIZE
5.0 (Windows 2000)NOTIFYICONDATA_V2_SIZE
Versions lower than 5.0NOTIFYICONDATA_V1_SIZE


请注意,cbSize中的长度不对时,会直接影响托盘图标能否显示,以及特性是否正常。

uFlags
新特性:
NIF_GUID
Windows 7 and later: 表示 guidItem  有效.
Windows Vista and earlier: 保留.
NIF_REALTIME
Windows Vista and later.  新增的特性,Balloon Tip若不能立即显示,则直接忽略掉。用于即时性要求较高的Balloon Tip。例如“你电话响了”(MSDN的例子), 要联合NIF_INFO这个特性使用才有效。
NIF_SHOWTIP
Windows Vista and later. 在uVersion 被设置为  NOTIFYICON_VERSION_4时,标准的ToolTip(鼠标指向托盘图标时的那个提示)会被取消,也会被 application-drawn、弹出式UI等替代。在这个情况下,我们可以设置NIF_SHOWTIP打开ToolTip。
uCallbackMessage
这个消息传递的参数在uVersion 设置为 NOTIFYICON_VERSION_4后,变为如下的意义。
LOWORD(lParam) 包含了消息类型,例如:NIN_BALLOONSHOW,NIN_POPUPOPEN,WM_CONTEXTMENU。有许多人想实现对用户单击气泡的消息响应,就是在这里做消息的响应。
HIWORD(lParam) 为uID,缩短为16位,我们用这个确定是哪一个托盘图标的消息。
GET_X_LPARAM(wParam) 返回鼠标消息时,为鼠标的X坐标。如果消息从键盘产生,wParam 包含的坐标为目标ICON的左上角坐标, 其他消息时wParam 未定义 。
GET_Y_LPARAM(wParam) 返回鼠标消息时,为鼠标的Y坐标。其他意义与GET_X_LPARAM(wParam)相同。
hIcon
XP和之后的版本支持32BPP的ICON。最好同时提供16x16、32x32的图标。在追求高DPI图标的情况下,可以使用如下函数加载图标,但此函数仅可在Vista及以上版本的系统中使用,若在代码中包含此语句,即便不运行,程序也不可以在XP下运行。

  1. LoadIconMetric(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME), LIM_LARGE, &(nid.hBalloonIcon));
复制代码

szTip
Windows2000之后支持128字节,包括NULL。

uTimeout
该值在Vista及之后系统下无效,此时间由系统决定。在其他情况下:最大值30秒,最小值10秒。单位为毫秒。另外有一点,就是关于XP下气泡提示不会自动消失的情况。MSDN中也指出这是一个存在的现象,当用户没有操作时(任何操作,例如单击开始菜单之类),气泡是不会消失的(XP)。个人觉得,为了保险,还是自己使用定时关闭Balloon Tips的好…

uVersion
这是一个比较关键的值
0
Windows 2000之前的操作系统使用,也是默认值.
NOTIFYICON_VERSION
使用Windows 2000特性,用于Windows 2000以及之后版本.
NOTIFYICON_VERSION_4
使用当前系统风格,用于Vista与之后的版本.

若我们要实现气泡上的操作的响应,我们必须设定为NOTIFYICON_VERSION_4,才能在CallbackMessage中响应到关于气泡的消息。当然其他消息也要按照上文中说明的来提取消息类型。
还有一点关键就是,要实现uVersion的设置,必须调用
  1. Shell_NotifyIcon(NIM_SETVERSION,&nid);
复制代码
才能实现版本的更改。

dwInfoFlags
新特性:

NIIF_USER

Windows XP: 使用hIcon做Balloon Tip的标题图标。
Windows Vista and later:使用 hBalloonIcon 做Balloon Tip的标题图标。

NIIF_LARGE_ICON

Windows Vista and later. 使用大图标做Balloon Tip的标题图标,对应的图标大小为SM_CXICON x SM_CYICON,如果不设置这个标记,那么使用的是XM_CXSMICON x SM_CYSMICON 大小的图标。
使用旧版本定制图标的程序(NIIF_USER 标记,使用 hIcon) ,也就是XP下,必须提供 SM_CXICON x SM_CYICON 版本的 hIcon。 托盘区的hIcon会自动缩小至XM_CXSMICON x SM_CYSMICON 大小。
使用新版本定制图标的程序(NIIF_USER 标记,使用 hBalloonIcon) ,也就是Vista之后的版本,hBalloonIcon必须有 SM_CXICON x SM_CYICON 大小。
NIIF_RESPECT_QUIET_TIME
MSDN中指出,在当前用户处于"quiet time"时,不显示,直接忽略该Balloon Tip,亦不进入Balloon Tip的等待队列。
至于这个"quiet time"是指:用户第一次登陆他的账户后的几个小时之内(which is the first hour after a new user logs into his or her account for the first time),并且系统更新与清理之后也会进入"quiet time"。MSDN说,是为了在用户适应新的电脑与环境之时,不被打扰而提供的功能。当然,程序需要用户立即做出回应,可以不用此标记,例如插入一个USB设备,或者正在打印一个文档之类的信息。

guidItem
XP和之后系统的特性
Windows 7 and later: 需要一个已注册的GUID代替uID来指示托盘图标(MSDN推荐的做法),需要在uFlags中置位NIF_GUID。
Windows XP and Windows Vista:保留,必须为0。
若程序要同时运行在Vista 与 WIN7,必须在程序中判断系统版本,再指定该值。
若设置了GUID,GUID则代替uID来操作托盘图标,切记。
可以使用GUID生成工具例如Guidgen.exe来生成一个有效的GUID。

hBalloonIcon
Windows Vista and later. Balloon Tip标题图标句柄,dwInfoFlags 中NIIF_USER中置位后, 若改为为0,则用传统的Balloon Tip标题图标
  
以上就是NOTIFYICONDATA的一些新特性,以2010年12月11日官方英文MSDN为原版的翻译与一些自己的提示。

需注意的是,在网上许多资料对气泡不弹出的解决办法,都是对以下的宏定义进行修改:
  1. #ifndef WINVER // 指定要求的最低平台是 Windows Vista。
  2. #define WINVER 0x0600 // 将此值更改为相应的值,以适用于 Windows 的其他版本。
  3. #endif

  4. #ifndef _WIN32_WINNT // 指定要求的最低平台是 Windows Vista。
  5. #define _WIN32_WINNT 0x0600 // 将此值更改为相应的值,以适用于 Windows 的其他版本。
复制代码
若是按其更改为0x0501之后,类似 NOTIFYICON_VERSION_4,hBalloonIcon等这些6.0版本的宏定义与成员是不可直接使用的。
其实,这种解决办法是错误的,请设置该值为默认值。
气泡不能正常显示,正是上文提及的cbSize的值设置不正确所导致的。
因为在该宏定义为0x0600时,NOTIFYICONDATA结构声明为上文中贴出的结构,而笼统使用sizeof获取的大小,亦为这个大小。但在XP中运行时,Shell32.dll版本较低,NOTIFYICONDATA中类似hBalloonicon等成员不存在,长度不一致,从而导致托盘异常,气泡无法正常显示。为了正常显示托盘气泡,一定要设置正确的cbSize值。

下面给出一个CallbackMessage的函数例子,用于NOTIFYICON_VERSION_4时的消息处理,可以实现气泡单击,关闭之类的消息处理。

  1. LRESULT CUSB_sensorDlg::OnICON(WPARAM wParam,LPARAM lParam)
  2. {
  3. if(HIWORD(lParam) != uID)//uID为你指定的托盘图标uID,过滤其他托盘程序的消息
  4. return 0;
  5. switch(LOWORD(lParam))
  6. {
  7. case WM_RBUTTONUP://右键弹起
  8. //添加代码
  9. break;
  10. case WM_LBUTTONDBLCLK://双击左键的处理
  11. //添加代码
  12. break;
  13. case NIN_BALLOONUSERCLICK://用户单击气泡处理
  14. //添加代码
  15. break;
  16. }
  17. return 0;
  18. }
复制代码

再给出一个简单的大图标Balloon Tip例子:
  1. nid.cbSize=(DWORD)sizeof(NOTIFYICONDATA);
  2. nid.hWnd=this-> m_hWnd;
  3. nid.uID=IDR_MAINFRAME;
  4. nid.uFlags=NIF_ICON|NIF_MESSAGE|NIF_TIP|NIF_INFO | NIF_SHOWTIP;
  5. nid.uCallbackMessage=WM_ICON;//自定义的消息名称
  6. nid.hIcon=LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDR_MAINFRAME));
  7. wcscpy_s(nid.szTip, _T("标准ToolTips"));
  8. wcscpy_s(nid.szInfo,_T("气泡内容"));
  9. wcscpy_s(nid.szInfoTitle,_T("气泡标题"));
  10. nid.uVersion=NOTIFYICON_VERSION_4;//打开高版本
  11. nid.dwInfoFlags= NIIF_USER | NIIF_LARGE_ICON ; //打开自定义及大图标提示
  12. nid.hBalloonIcon=LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDR_MAINFRAME));
  13. Shell_NotifyIcon(NIM_ADD,&nid);//在托盘区添加图标
  14. Shell_NotifyIcon(NIM_SETVERSION,&nid);//设置vista效果的balloon tips
复制代码

另外,可能有人不知道如何立即关闭BalloonTip,只要令szInfo的内容为空,再修改图标,就可以立即关闭提示。  

最后再提醒一点,使用了新特性的程序,很可能在XP下无法使用。所以,想让自己的程序拥有美丽的外观的同时,又能在XP下正常运行,一定要善用上文提及的函数判断shell32.dll版本之后,再配置NOTIFYICONDATA结构体。

虽然大图标提示很美观,但也不要滥用提示,除非是自用的,不然用户很可能彻底告别你的程序!

若是有帮助,留个言支持下,我会继续写一些个人的经验与大家分享~
欢迎留言交流问题与经验~若是有错误,还请指正!




本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/kvs112219/archive/2010/12/11/6069936.aspx

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

评分

参与人数 1威望 +2 收起 理由
Local + 2 精品文章

查看全部评分

发表于 2011-1-21 07:56:14 | 显示全部楼层
太棒了  这段时间我正想写一个完美封装Shell_NotifyIcon的类模块,原先只适用XP,现在看来有机会支持win7了
回复 支持 反对

使用道具 举报

发表于 2011-1-21 07:59:01 | 显示全部楼层
即便不运行,程序也不可以在XP下运行。


这句真深奥 看不懂
回复 支持 反对

使用道具 举报

发表于 2011-1-21 08:02:38 | 显示全部楼层
我的想法是,多定义几个结构体比如NOTIFYICONDATA_XP,NOTIFYICONDATA_Vista,NOTIFYICONDATA_Win7,先取得系统版本号得到运行模式,然后在Shell_NotifyIcon传递指针过去,这样可以吗?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2011-1-21 11:27:07 | 显示全部楼层
Local 发表于 2011-1-21 07:59
这句真深奥 看不懂

这句话的意思是,由于LoadIconMetric函数只有Vista及以上系统才支持,具体参考这里 所以如果你的程序是准备在XP系统上运行的话,我估计连编译都无法通过,会发生找不到对应的函数入口~~~
回复 支持 反对

使用道具 举报

 楼主| 发表于 2011-1-21 11:30:29 | 显示全部楼层
Local 发表于 2011-1-21 08:02
我的想法是,多定义几个结构体比如NOTIFYICONDATA_XP,NOTIFYICONDATA_Vista,NOTIFYICONDATA_Win7,先取得 ...

我也是这么想的,行不行试试就知道了,别忘了弄出来后开源啊
回复 支持 反对

使用道具 举报

发表于 2011-1-21 16:46:58 | 显示全部楼层
回复 红色狂想 的帖子

VB6没这事情,因为所有API调用都是动态加载的……
回复 支持 反对

使用道具 举报

发表于 2011-1-21 23:40:47 | 显示全部楼层
本帖最后由 Local 于 2011-1-21 23:41 编辑

回复 红色狂想 的帖子

哈哈 这下vb的动态调用api就有功效了~


至于c++,动态调用api的例子多得是~c++调用NtShutdownSystem就是一个动态调用的例子
回复 支持 反对

使用道具 举报

发表于 2011-1-21 23:43:34 | 显示全部楼层
回复 红色狂想 的帖子

其实我很纠结的是你的句子主干:即使不运行,也不可以运行
回复 支持 反对

使用道具 举报

头像被屏蔽
发表于 2011-5-7 22:23:17 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

文字版|手机版|小黑屋|VBGood  

GMT+8, 2022-7-3 02:24

VB爱好者乐园(VBGood)
快速回复 返回顶部 返回列表