有些应用软件为便于提醒用户和方便用户操作,会在系统托盘区生成属于自己的托盘图标,这样用户可以通过该图标弹出的气泡提示了解对应软件的运行状态,或通过关联的菜单便捷的操作该软件。但在某些情况下,应用程序可能会遭遇异常关闭的情况而导致来不及删除对应的系统托盘图标而将其遗留在系统托盘区中,当再次运行该软件时会发现托盘区出现多个同样的图标。那么如何删除无效/指定的系统托盘区图标呢?
首先,我们需要找到系统托盘栏所在的窗口,由于我们不清楚需要删除的图标是在普通系统托盘区中还是在溢出系统托盘区中,所以需要把这两个窗口都找出来:
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);
}
}returnhWnd;
}
2.查找溢出系统托盘区窗口
HWND FindOverflowTrayWindow()
{
HWND hWnd=NULL;
//查找NotifyIconOverflowWindow窗口
hWnd= ::FindWindow(_T("NotifyIconOverflowWindow"), NULL);if (hWnd !=NULL)
//查找ToobarWindow32窗口
hWnd= FindWindowEx(hWnd, NULL, _T("ToolbarWindow32"), NULL);returnhWnd;
}
其次,查询窗口所含图标数量并遍历所有的图标找到目标图标后通过向Shell_NotifyIcon函数传入NIM_DELETE参数删除目标图标
VOID DeleteInvalidTrayIcon(HWND hWnd)
{if (hWnd ==NULL)return;structTRAYDATA
{
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);
}
}}
在软件启动时可通过上述方法找到之前异常关闭时没有来得及删除的托盘图标并清理之,从而保证系统托盘区只保留与该软件相关的唯一有效图标。