当前位置: 首页 > 编程日记 > 正文

一种清除windows通知区域“僵尸”图标的方案——问题分析

通知区域名称有趣的历史

假如说到windows通知区域,可能很多人还是不清楚它是什么。如果改称Tray区域,可能有人就懂了。如果再白话点,叫它“托盘”或者“系统托盘”,可能会有更多的人猜到它是windows什么部位。现在我们揭开它真实的面纱,以windows7系统为例,下图就是它的通知区域。 (转载请指明出于breaksoftware的csdn博客)


其实,我们叫通知区域为“托盘”或者“系统托盘”是错误的。这个错误并非来源于中文翻译,而是来源于windows发展史上人们对其错误的认识。后来,这个命名也影响了中国一批程序员。我这儿要摘录一个微软老员工的回忆录《The Old New Thing》(中文名《windows编程启示录》)一书中关于这个错误认识起源的一段,还是蛮有意思的。

“后来,我们将通知图标添加到任务栏中。”

“我认为人们开始将通知区域叫作系统托盘是因为在Windows95中包含了一个systray.exe的程序,这个程序在通知区域中显示了一些图标,如音量控制,PCMCIA(在当时是叫这个名字)的状态、电池的电量表等。如果你终止了systray.exe,那么这些通知图标也将会消失。因此人们就认为,‘啊,systray程序一定是管理这些图标的组件,我敢打赌这个组件的名字就叫作“系统托盘”’。于是这个误解就形成了,而我们这十几年来一直都在努力澄清这个误解。”

“更糟糕的是,其他的团队(Shell之外的团队)也错误地使用了这个词,并且开始在他们自己的文档和示例程序里面都使用了系统托盘这个词,其中有一些地方甚至错误地声称系统托盘就是通知区域的正式名称。”

“有人可能会问,‘你为什么要关心这个名字的正误?既然现在所有的人都叫这个名字,你也可以随波逐流嘛。’”

“如果每个人都叫错了你的名字,你会乐意吗?”

其实我觉得,如果微软真的想彻底摒弃“系统托盘”这个名称,最好是从现在做起,将通知区域的一些信息都修改成和Tray这个单词无关。可是,我们使用Spy++查看Windows7任务栏的组成时就会发现,Tray这个单词无处不在啊!



“僵尸图标”

说了这么多历史故事,我们再回到我们这篇博文要讲述的问题上。其实这个问题,依旧是个历史问题。还好,我发现vista之后的系统上,微软已经意识并修复了这个设计缺陷。我们看下下面的场景

很多使用Windows的人可能都遇到过这个问题:通知区域出现了N个相同的“僵尸”图标。如果我们有意或者无意让光标划过这些图标时,这些图标会悄然消失。我们对这种现象,往往是疑惑一下就抛之脑后。然而,目前我在项目中就接到一个需求:把这些“僵尸”图标自动消失。出于我们产品的设计,我们存在出现这么多“僵尸”图标的场景,于是为了优化用户体验,我需要找到一种方法去解决这种体验问题。


通知区域图标的正常生死过程

首先要分析一下这个问题出现的原因。一般来说,一个程序在创建时,可能会在通知区域创建一个图标。

一般初始化图标

创建图标之前,我们需要初始化一个图标

NOTIFYICONDATA m_NotifyIcon;
……
m_NotifyIcon.cbSize = sizeof(m_NotifyIcon);
m_NotifyIcon.uFlags = NIF_ICON | NIF_TIP;
m_NotifyIcon.uVersion = NOTIFYICON_VERSION; // xp
m_NotifyIcon.hWnd = m_hWnd;
m_NotifyIcon.hIcon = m_hIcon;
std::wstring wstrInfo = L"中A英1文"; // 故意取一个晦涩的名字
wmemcpy_s(m_NotifyIcon.szTip, ARRAYSIZE(m_NotifyIcon.szTip), wstrInfo.c_str(), wstrInfo.length()+1 );

这个地方需要注意的是下面几个参数:

  1. uFlags。我们只是设置了NIF_ICON和NIF_TIP,因为我们需要让我们的通知区域图标变得与众不同,故通过指定这两个标志分别告知系统:我们要设定图标和Tip文字。这个属性我们会在处理Windows7系统上“僵尸”图标的时候再次提起。
  2. hWnd。因为我们图标要相应用户的点击,并将相应消息传递给我们主窗口,所以我们此时要绑定主窗口句柄。这个属性我们会在未来介绍一个特定场景时再次提到。
  3. szTip。我们故意给我们这个图标取了一个晦涩的Tip,这样我们在之后查找“僵尸图标”时将有据可凭。

图标添加到通知区域

图标初始化后,我们要将图标增加到通知区域
Shell_NotifyIcon(NIM_ADD, &m_NotifyIcon);

这个图标是可以表明“这个进程还活着”;而且在无界面展现时,让用户方便唤起界面或者执行相应的功能。比如QQ的通知区域图标,它的存在表明QQ进程还是存在的。我们可以左键双击之,可以让主界面展现出来;还可以右击之,可以出现很多快捷功能键


图标从通知区域剔除

相应的,如果进程退出,应该通知系统通知区域:要将我设置的通知区域图标删除,因为我马上要退出了。

Shell_NotifyIcon(NIM_DELETE, &m_NotifyIcon);

如果一切都如此按照规律的“正常生死”,也就没有之前提出的问题。可是,出于策略考虑以及一些异常情况,进程的意外死亡还是不可避免的。这样,如果出现连续的意外死亡场景,系统通知区域就会残留很多“僵尸”图标。为了大战这些“僵尸”,我们需要找到这些“僵尸”的家,然后对“僵尸”各个击破。于是,我们要看下各系统下通知区域的树状结构图。

XP、Win7下通知区域的结构

先使用SPY++看下XP下任务栏即通知区域的结构

      #32769 (桌面)- Shell_TrayWnd- Button- TrayNotifyWnd- TrayClockWClass- SysPager- ToolbarWindow32(我们关心的,其直接显示在桌面上)- Button- CiceroUIWndFrame- MSTaskSwWClass- ToolbarWindow32- ReBarWindow32

SysPager下类名为ToolbarWindow32的控件就是系统通知区域。非常庆幸,XP下只有这么一个通知区域,而且这个通知区域一直是可见的(Win7下有个不可见的通知区域)。

再看下Win7的通知区域结构

#32769 (桌面)- Shell_TrayWnd- TrayNotifyWnd- TrayClockWClass- TrayShowDesktopButtonWClass- SysPager- ToolbarWindow32(我们关心的,其直接显示在桌面上)- ToolbarWindow32(其隐藏在桌面上,通过SendTimeout发送TB_BUTTONCOUNT不能获取其个数)- Button- ReBarWindow32- CiceroUIWndFrame- MSTaskSwWClass- MSTaskListWClass
Win7的通知区域相对于XP有点复杂,其中我们一直可见的通知区域的树状结构和XP上是一致的。但是Win7上多出了一个隐藏的通知区域,它和SysPager同级


        针对XP和Win7上都可见的通知区域,我们可以通过如下代码找到相应区域去清理

VOID CKillRunProcessDlg::VisitNotificationArea()
{HWND hwndChildAfter = NULL;DWORD dwMaxLoopCount = MAXLOOPCOUNT;do {// 保守性编程,防止死循环dwMaxLoopCount--;HWND hTrayWnd = NULL;hTrayWnd = ::FindWindowEx( NULL, hwndChildAfter, L"Shell_TrayWnd", NULL);if ( NULL == hTrayWnd ) {break;}// 找到了窗口类为 Shell_TrayWnd的窗口,// 但是不保证找到的就是Notification所在区域的,// 所以记录下当前找到的,之后继续找hwndChildAfter = hTrayWnd;HWND hTrayNotifyWnd = ::FindWindowEx(hTrayWnd, NULL, L"TrayNotifyWnd", NULL );if ( NULL == hTrayNotifyWnd ) {// 继续找符合条件的Shell_TrayWnd类,然后再在其下找类为TrayNotifyWnd的子窗口continue;}// 这个窗口只能在Win7系统中可以找到HWND hToolBar32Ex = ::FindWindowEx( hTrayNotifyWnd, NULL, L"ToolbarWindow32", NULL );// 在win7 xp下都可以找到该树结构HWND hSysPager = ::FindWindowEx( hTrayNotifyWnd, NULL, L"SysPager", NULL );HWND hToolBar32Showed = NULL;if ( NULL != hSysPager ) {  hToolBar32Showed = ::FindWindowEx( hSysPager, NULL, L"ToolbarWindow32", NULL );}else {// 找不到该树结构// 则继续找符合条件的Shell_TrayWnd类,然后再在其下找类为TrayNotifyWnd的子窗口continue;;}if ( m_bVistaLater ) {if ( NULL == hToolBar32Showed || NULL == hToolBar32Ex ) {// 都要有,否则不是合法的continue;}}else {if ( NULL == hToolBar32Showed ) {continue;}}if ( FALSE == IsExplorerProcess(hToolBar32Showed) ) {if ( ERROR_NOT_FOUND != ::GetLastError() ) {// 不是Explorer进程,则继续寻找continue;}}if ( NULL != hToolBar32Showed ) {// 清理通知区域CleareIcons(hToolBar32Showed);}} while ( dwMaxLoopCount > 0 );
}

鉴于XP的通知区域的结构简单性,我决定先从XP系统入手。其实XP上的解决方案是多种的,也是非常有意思的。详细的分析过程可以参看下篇博文《一种清除windows通知区域“僵尸”图标的方案——XP系统解决方案》。

相关文章:

Apache2.4+Tomcat7集群搭建

一、安装jdk、Tomcat、Apache1.安装jdk1.7cd /home/java/software #把软件下载到/home/java/software目录下,将应用安装到/home/java目录下。 wget http://download.oracle.com/otn/java/jdk/7u80-b15/jdk-7u80-linux-x64.tar.gz tar -zxvf jdk-7u80-linux-x64.tar…

一种清除windows通知区域“僵尸”图标的方案——XP系统解决方案

XP下“僵尸”图标的解决方案 从《一种清除windows通知区域“僵尸”图标的方案——问题分析》(以后简称《问题分析》)一文中分析的通知区域结构可以看出,XP的通知区域结构是相对简单的。如果我们解决了XP下的问题,那么Win7上的问题…

《评人工智能如何走向新阶段》后记(再续12)

由AI科技大本营下载自视觉中国151. 新一代人工智能研究方向: (1)研究新一代人工智能基础理论(机理、模型和算法);(2)研发面向需求的共性技术(以神经网络和算法为核心、数据和硬件为基…

正则表达式测试工具 Regex Tester 的使用方法

2019独角兽企业重金招聘Python工程师标准>>> 正则表达式测试工具“RegexTester”,下载地址:http://www.oschina.net/p/regextester 一、关于本文 今天的工作中遇到了一些正则表达式,我需要检验它们是否正确,不过我对自…

一种清除windows通知区域“僵尸”图标的方案——Windows7系统解决方案

Windows7下“僵尸”图标的解决方案 从《一种清除windows通知区域“僵尸”图标的方案——问题分析》(以后简称《问题分析》)一文中分析的通知区域结构可以看出,Windows7的通知区域比XP通知区域多出了一个“临时”系统通知区域(转载…

《评人工智能如何走向新阶段》后记(再续13)

由AI科技大本营下载自视觉中国161. 引自美国科技媒体TNW记者对美欧企业主管与AI专家的访谈录摘要,谈到2020年AI的八大趋势: ①人工智能将使医疗保健更准确、成本更低; ②可解释性和信托及AI伦理将受到更多关注; ③在人工智能领…

在特定情况下的简单SSO实现方案

最近需要实现类似单点登录的功能。情况是这样的,最初在做网站A,做着做着,要做网站B了,要求与网站A完全分开作为两个应用,但用户数据要求与网站A保持一致,也要求用户在网站A登录后,转到网站B时不…

为创业者保驾护航 “无安全 不创业” 安全狗全国路演北京站

2019独角兽企业重金招聘Python工程师标准>>> 2015年上半年,网络安全问题毫无疑问已经成为了互联网行业关注的重点。在短短一年多的时间里,网络安全问题就从隐患转而呈现出爆发之势,即使是网易、支付宝、携程这样的互联网行业巨头也…

一种将快捷方式从开始菜单“常用应用”的中去除的方法

当我们安装一款软件的时候,这款软件的一些快捷方式可能被设置到开始菜单的“常用应用”区域。但是,如果是“卸载”快捷方式被“钉”到该区域,就会造成非常不好的体验。毕竟把“卸载”接口暴露得如此醒目,如同把该款软件的地狱大门…

ISA---不能访问网址或是多次刷新才能访问的解决方法一则

当你安装ISA2006在WINDOWS 2003 SERVER上,并打上SP2补订时。遇SNAT客户端不能访问WEB,但能PING通,能TELNET通,也能访问QQ或是MSN的问题时可以利用以下方法解决。同时,如果你遇到在此环境下,客户端访问外部网…

《评人工智能如何走向新阶段》后记(深谈人工智能发展前沿)

由AI科技大本营下载自视觉中国来自国内外的跟贴留言 深谈人工智能发展前沿 自从我们发表《评人工智能如何走向新阶段》一文以来,至今约5个月,引来了中外专家、草根们的大量跟贴留言(也有人转录他人的公开言论作为跟贴来发表的)。…

URAL 2027 URCAPL, Episode 1 (模拟)

题意&#xff1a;给你一个HxW的矩阵&#xff0c;每个点是一个指令&#xff0c;根据指令进行一系列操作。 题解&#xff1a;模拟 #include<cstdio> #include<algorithm> using namespace std;const int maxn 101; char G[maxn][maxn];int dx[] {-1,0,1, 0}; int d…

使用WinHttp接口实现HTTP协议Get、Post和文件上传功能

我实现了一个最新版本的接口&#xff0c;详见《实现HTTP协议Get、Post和文件上传功能——使用WinHttp接口实现》。还有基于libcurl实现的版本《实现HTTP协议Get、Post和文件上传功能——使用libcurl接口实现》。以下是原博文&#xff1a; 我们在做项目开发时&#xff0c;往往会…

收藏 | 一文带你总览知识蒸馏,详解经典论文

「免费学习 60 节公开课&#xff1a;投票页面&#xff0c;点击讲师头像」作者&#xff1a;凉爽的安迪来源 | 深度传送门&#xff08;ID&#xff1a;deep_deliver&#xff09;【导读】这是一篇关于【知识蒸馏】简述的文章&#xff0c;目的是想对自己对于知识蒸馏学习的内容和问题…

[工具推荐]用了TrueCrypt 再无难掩之隐

缘起&#xff1a;混在网络n多年了&#xff0c;手头总有些东西不想被别人看到的东西&#xff0c;由于小弟人品好&#xff0c;相貌佳&#xff0c;总有很多朋友喜欢用我的电脑玩啊玩啊……。 近日&#xff0c;冠希、柏芝等前辈以身示法&#xff0c;为我等上了很好一堂关于隐私保护…

利用phpmailer类邮件发送

<?phprequire("class.phpmailer.php"); //下载的文件必须放在该文件所在目录$mail new PHPMailer(); //建立邮件发送类$address "接收方邮箱"; //接收方地址$mail->IsSMTP(); //使用SMTP方式发送$…

据说这是大多数人【减肥】的真实写照

有句诗说得好 “冬天不减肥&#xff0c;夏天徒伤悲” 在这个人人储存脂肪的季节绝对是你甩掉脂肪的好时机&#xff08;毕竟这是一个拼颜值的时代颜值是天生的&#xff0c;可是身材绝不能输&#xff09;但是 据说大多数人的减肥经历其实是这样的减肥第一步管住嘴&#xff0c;迈开…

PE文件和COFF文件格式分析——导出表的应用——一种摘掉Inline钩子(Unhook)的方法

在日常应用中&#xff0c;某些程序往往会被第三方程序下钩子(hook)。如果被下钩子的进程是我们的进程&#xff0c;并且第三方钩子严重影响了我们的逻辑和流程&#xff0c;我们就需要把这些钩子摘掉(Unhook)。本件讲述一种在32位系统上&#xff0c;如何摘掉API钩子的思路和方法。…

设置列表字段为主键

转贴:Sample event handler to set a field as a pr imary key (enforce no duplicates) Got this as a request from a reader- how to prevent users from adding items with same titles as ones that already exist in the list. Codeusing System;using System.Collectio…

谁登录了你的linux

最近有一台数据库服务器自动重启。查了一下相关登录信息&#xff1a;查看linux下的用户登录日志&#xff0c;包括用户登录时所用的主机的ip&#xff1a;more /var/log/secure who /var/log/wtmp干了些什么&#xff1f; root账户下输入su - username 切换到username下输入 histo…

一种使用GDI+对图片尺寸和质量的压缩方法

今天同事向我询问图片压缩的算法&#xff0c;我想起大概两三年前做过的一个项目。其中包含了尺寸和质量两种压缩算法&#xff0c;并且支持JPEG、bmp、PNG等格式。今天把这段逻辑贴出来&#xff0c;供大家参考。&#xff08;转载请指明来源于breaksoftware的CSDN博客&#xff09…

.NET企业级应用架构设计系列之应用服务器

本文属spanzhang&#xff08;张友邦&#xff09;原创&#xff0c;发布地址为&#xff1a;http://blog.csdn.net/spanzhang。转载或引用请注明原文之出处&#xff0c;谢谢&#xff01; .NET企业级应用架构设计系列之开场白 .NET企业级应用架构设计系列之技术选型 这里要说到的…

编程语言发展70年,用50种不同语言输出「Hello World」

「免费学习 60 节公开课&#xff0c;投票页面&#xff0c;点击讲师头像」作者 | Sylvain Saurel译者 | 风车云马责编 | 屠敏【导读】历经 70 年&#xff0c;不断出现的编程语言为开发者解决了哪些难题&#xff1f;其存在又有怎样的特性&#xff1f;本文将以「Hello World」为例…

函数循环的理解

2019独角兽企业重金招聘Python工程师标准>>> var ulObjdocument.getElementById("box"); var lisObjulObj.getElementsTagname("li"); for(var i0;i<lisObj.length;i) { lisObj[i].οnclickfunction()//循环时对应节点绑定事件&#xff0c;事…

从LeNet到GoogLeNet:逐层详解,看卷积神经网络的进化

「免费学习 60 节公开课&#xff1a;投票页面&#xff0c;点击讲师头像」作者 | MrCharles来源 | CSDN原力计划获奖作品&#xff08;*点击阅读原文&#xff0c;查看作者更多文章&#xff09;前言深度学习的兴起使卷积神经网络在计算机视觉方面大放异彩&#xff0c;本文将按时间…

Windows客户端C/C++编程规范“建议”——前言

前言 工作中接触了很多编程规范。其中最有意思的是&#xff0c;公司最近发布了一版C/C编程规范&#xff0c;然后我看到该规范的最后一段时&#xff0c;有这么一句&#xff1a;“该规范不适用于Windows平台开发”。看来这份规范是由做其他平台开发的同学制定的。那么做Windows开…

storm入门教程 第一章 前言[转]

1.1 实时流计算 互联网从诞生的第一时间起&#xff0c;对世界的最大的改变就是让信息能够实时交互&#xff0c;从而大大加速了各个环节的效率。正因为大家对信息实时响应、实时交互的需求&#xff0c;软件行业除了个人操作系统之外&#xff0c;数据库&#xff08;更精确的说是…

Windows客户端C/C++编程规范“建议”——函数

1 函数 1.1 代码行数控制在80行及以内 等级&#xff1a;【要求】 说明&#xff1a;每个函数的代码行数控制应该控制在80行以内。如果超过这个限制函数内部逻辑一般可以拆分。如果试图超过这个标准&#xff0c;请列出理由。但理由不包含如下&#xff1a; 无法拆分。流程内部…

把自己朝九晚五的工作自动化了,有错吗?

作者 | Brian Merchant译者 | 谭开朗编辑 | 屠敏来源 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;【导读】用代码让自己工作开启自动化之际&#xff0c;是否意味着自己将面临被解雇的风险&#xff1f;2016年&#xff0c;Reddit上出现了一个匿名的帖子&#xff0c;内…