删除系统托盘区中的指定图标

有些应用软件为便于提醒用户和方便用户操作,会在系统托盘区生成属于自己的托盘图标,这样用户可以通过该图标弹出的气泡提示了解对应软件的运行状态,或通过关联的菜单便捷的操作该软件。但在某些情况下,应用程序可能会遭遇异常关闭的情况而导致来不及删除对应的系统托盘图标而将其遗留在系统托盘区中,当再次运行该软件时会发现托盘区出现多个同样的图标。那么如何删除无效/指定的系统托盘区图标呢?

首先,我们需要找到系统托盘栏所在的窗口,由于我们不清楚需要删除的图标是在普通系统托盘区中还是在溢出系统托盘区中,所以需要把这两个窗口都找出来:

1.查找普通系统托盘区窗口

HWND FindTrayWindow()
{
  HWND hWnd = NULL;
  HWND hWndPage = NULL;

  // 查找Shell_TrayWnd窗口
  hWnd = ::FindWindow(_T("Shell_TrayWnd"), NULL);
  if (hWnd != NULL) {    // 查找TrayNotifyWnd窗口
    hWnd = ::FindWindowEx(hWnd, NULL, _T("TrayNotifyWnd"), NULL);
    if (hWnd != NULL) {      // 查找SysPager窗口.
      hWndPage = ::FindWindowEx(hWnd, NULL, _T("SysPager"), NULL);
      if (hWndPage != NULL)        // 查找ToobarWindow32窗口.
        hWnd = ::FindWindowEx(hWndPage, NULL, _T("ToolbarWindow32"), NULL);
      else
        // Win2000没有SysPager窗口,可直接查找ToobarWindow32窗口.
        hWnd = ::FindWindowEx(hWnd, NULL, _T("ToolbarWindow32"), NULL);
    }
  }
  return hWnd;
}

2.查找溢出系统托盘区窗口

HWND FindOverflowTrayWindow()
{
  HWND hWnd = NULL;  // 查找NotifyIconOverflowWindow窗口
  hWnd = ::FindWindow(_T("NotifyIconOverflowWindow"), NULL);
  if (hWnd != NULL)    // 查找ToobarWindow32窗口
    hWnd = FindWindowEx(hWnd, NULL, _T("ToolbarWindow32"), NULL);

  return hWnd;
}

其次,查询窗口所含图标数量并遍历所有的图标找到目标图标后通过向Shell_NotifyIcon函数传入NIM_DELETE参数删除目标图标

VOID DeleteInvalidTrayIcon(HWND hWnd)
{
  if (hWnd == NULL)
    return;

  struct TRAYDATA
  {
    HWND hWnd;
    UINT uID;
    UINT uCallbackMessage;
    DWORD Reserved1[2];
    HICON hIcon;
    DWORD Reserved2[3];
    TCHAR szExePath[MAX_PATH];
    TCHAR szTip[128];
  };

  DWORD dwProcessID = 0;
  DWORD dwButtonCount = 0;
  HANDLE hProcess = INVALID_HANDLE_VALUE;
  TBBUTTON tbButton;
  LPVOID pTB;
  TRAYDATA td;
  NOTIFYICONDATA nid;
  TCHAR szSynTPEnhPath[MAX_PATH] = { 0 };
  TCHAR *pszApplicationName;

  // 查询指定窗口所含图标数,每个图标对应一个按钮
  dwButtonCount = (DWORD)::SendMessage(hWnd, TB_BUTTONCOUNT, 0, 0);
  if (dwButtonCount == 0)
    return;

  // 获取窗口所在的线程
  if ((::GetWindowThreadProcessId(hWnd, &dwProcessID) != 0)
    && (dwProcessID != 0)) {
    hProcess = ::OpenProcess(PROCESS_ALL_ACCESS | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE,
      FALSE, dwProcessID);
    if (hProcess != NULL) {
      pTB = ::VirtualAllocEx(hProcess, NULL, sizeof(TBBUTTON), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
      if (pTB != NULL) {
        // 遍历所有图标并匹配目标信息,从而找到目标图标并删除之
        for (DWORD i = 0; i < dwButtonCount; i++) {
          if ((SendMessage(hWnd, TB_GETBUTTON, i, (LPARAM)pTB) == TRUE) &&
            (::ReadProcessMemory(hProcess, pTB, &tbButton, sizeof(TBBUTTON), NULL) != 0) &&
            (::ReadProcessMemory(hProcess, (LPVOID)tbButton.dwData, &td, sizeof(TRAYDATA), NULL) != 0)) {// 如果托盘图标的szExepath或szTip包含特定的信息,该图标就是我们准备清除的图标,找到并删除它
            if (_tcsstr(td.szExePath + 2, szSpecifiedApplictionName) &&
              _tcsstr(td.szTip + 2, szSpecifiedTip)) {
              nid.cbSize = NOTIFYICONDATA_V2_SIZE;
              nid.uID = td.uID;
              nid.hWnd = td.hWnd;
              nid.hIcon = td.hIcon;
              nid.uCallbackMessage = td.uCallbackMessage;
              nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
              ::Shell_NotifyIcon(NIM_DELETE, &nid);
            }
          }
        }
        ::VirtualFreeEx(hProcess, pTB, sizeof(TBBUTTON), MEM_FREE);
      }
      ::CloseHandle(hProcess);
    }
  }
}

在软件启动时可通过上述方法找到之前异常关闭时没有来得及删除的托盘图标并清理之,从而保证系统托盘区只保留与该软件相关的唯一有效图标。

时间: 2024-08-01 19:12:57

删除系统托盘区中的指定图标的相关文章

利用批处理文件删除系统托盘上的图标(适用于Windows各个版本)

对于我这种强迫症患者来说,如果我已经删除了一些软件,但是系统托盘里面还有它,我会很难受.所以,没办法,必须想办法把它清除掉,还自己一片安宁!!!不知各位是否遇到过和我一样的问题,下面贴一段批处理文件的代码供强迫症患者使用!!!! @echo off taskkill /im explorer.exe /f reg delete "HKCU\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayN

.net 删除缓存的key中带有指定字符的方法

/// <summary>        /// 删除带有指定字符的缓存        /// </summary>        /// <param name="pre"></param>        public void Refresh(string pre)        {            System.Web.Caching.Cache _cache = HttpRuntime.Cache;            I

使用QT创建系统托盘

使用QT来创建一个系统托盘,事实上是一件很简单的事.为什么这么说?一是因为QT文档给出了比较详细的例子,二是QT的结构比较优雅,设计风格统一.但是在动手之前,我们要从哪里下手?虽然QT文档给出了一个比较详细的例子,但由于功能较多,所以看起来比较费劲.我们在这只是抽丝剥茧,只实现系统托盘这一个功能. 首先,使用QT creator创建一个GUI工程,继承于QDialog还是QMainWindow还是QWidget都无所谓.我们以继承QMainWindow为例说明.创建完毕之后,creator将自动

WPF 系统托盘 图标闪烁

WPF消息通知 系统托盘,图标闪烁 1 using System.Windows.Forms; 2 3 using System.Windows.Threading; 4 5 public partial class Window : Window 6 { 7 private NotifyIcon notifyIcon; 8 DispatcherTimer icoTimer = new DispatcherTimer(); 9 string icoUrl = @"../../Red.ico&qu

【原创】Git删除暂存区或版本库中的文件

0 基础 我们知道Git有三大区(工作区.暂存区.版本库)以及几个状态(untracked.unstaged.uncommited),下面只是简述下Git的大概工作流程,详细的可以参见本博客的其他有关Git的文章[链接]. (1)打开你的项目文件夹,除了隐藏的.git文件夹,其他项目文件位于的地方便是工作区,工作区的文件需要添加到Git的暂存区(git add),随后再提交到Git的版本库(git commit). (2)首次新建的文件都是untracked状态(未跟踪),此时需要git add

wpf 让图标显示在系统托盘

上次做wpf时想把程序运行的图标显示在任务栏,结果发现wpf的系统托盘和winform的不一样,以前的方法不管用了. 网上搜的好多都是winform的资料,wpf的很少. 最后我把我现在做好的整理分享下,方便别人,也方便自己. 文章难免有些错误,欢迎指正,下面代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Windows; using System.Windo

Mac OS X: 删除系统中的Grow!软件

系统版本和系统配置:  OS X Yosemite 10.10.4 故障现象:可能是由于安装过盗版软件,所以系统被安装了Grow!软件. 解决方法: 1.可能是由于安装过盗版软件,所以在"系统偏好设置"中发现,被安装了Grow!软件. 2.Grow!简介: 3.打开"Grow!"配置界面. 4.Grow!的通知栏效果预览 5.在"系统偏好设置"中,右击"Grow!",选择卸载,会弹出一个确认卸载的对话框. 6.确认卸载之后,G

删除string类型字符串中指定字符串段

1.实现背景 在插入list行时用邮件的MessageID给对应行命名. 在回复全部邮件时,收件人变为之前收件人中出去“自己”同时加入之前发件人,抄送人还是之前的抄送人,密送人不用管,直接不用带. 在“回复全部”按钮响应函数里面 CListUI* pList = static_cast<CListUI*>(m_PaintManager.FindControl(_T("middle_comlumn_header1")));//拿到list控件指针            int

转: javascript 从数组中删除指定值(不是指定位置)的元素

例如数组{1,2,3,4,5} 要把数组里面的3删除得到{1,2,4,5} <script type="text/javascript"> Array.prototype.indexOf = function(val) { //prototype 给数组添加属性 for (var i = 0; i < this.length; i++) { //this是指向数组,this.length指的数组类元素的数量 if (this[i] == val) return i;