如果自己开发的WPF或Winform程序,有显示托盘图标的话,当程序异常结束时(如因为未处理的异常而导致程序崩溃,或者直接使用VS的停止调试按钮结束程序),失效的托盘图标就会一直残留在托盘窗口中。虽然这些失效的托盘图标不会影响什么,但过多累积的失效图标,看起来也确实会给人不好的观感,尤其是对于有强迫症的同学。
那么有没有办法自动清除这些失效的托盘图标呢?答案是有的。实际上你会发现,当鼠标滑过这些失效图标时,它们会自动消失掉。所以我们的办法就是,通过代码的形式,模拟鼠标滑过的动作,从而达到自动清除失效图标的目的。
实际上,网上有现成的代码,不过很多都是抄来抄去,无法使用的代码。这些代码中可能只是存在一些小的问题,但它就是没法用。好了,废话不多说,下面直接上我自己运行OK且正在使用的代码,供大家参考/复制...
//该类用于刷新托盘窗口(包括显示区域和溢出区域),达到清除残留图标的目的。
//原理:在程序每次开启前,向托盘窗口发送鼠标滑过消息WM_MOUSEMOVE,使其刷新,在此过程中,无效的托盘图标即会消失。
public class TrayArea
{
//只读字段,用于获取当前系统语言信息(是中文系统,还是英文系统)
private static readonly System.Globalization.CultureInfo _CultureInfo = System.Globalization.CultureInfo.InstalledUICulture;
//定义struct,用于存放托盘区域的位置信息
private struct Rect
{
public int left, top, right, bottom;
}
[DllImport("user32.dll")]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string className, string windowTitle);
[DllImport("user32.dll")]
private static extern bool GetClientRect(IntPtr handle, out Rect rect);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr handle, uint message, int wParam, int lParam);
/// <summary>
/// 刷新托盘区域。
/// </summary>
/// <returns>Task</returns>
public static Task RefreshAsync() //异步形式函数
{
return Task.Run(() => Refresh());
}
/// <summary>
/// 刷新托盘区域。
/// </summary>
public static void Refresh()
{
//一层一层的父窗口的Handle
IntPtr trayWndHandle = FindWindow("Shell_TrayWnd", null);
IntPtr trayNotifyWndHandle = FindWindowEx(trayWndHandle, IntPtr.Zero, "TrayNotifyWnd", null);
IntPtr sysPagerHandle = FindWindowEx(trayNotifyWndHandle, IntPtr.Zero, "SysPager", null);
//刷新用户提示通知区域
string windowTile_UserPromotedNotificationArea = _CultureInfo.Name == "zh-CN" ? "用户提示通知区域" : _CultureInfo.Name.StartsWith("en") ? "User Promoted Notification Area" : null; //窗口标题,目前仅支持简体中文和英文
IntPtr windowHandle_UserPromotedNotificationArea = FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", windowTile_UserPromotedNotificationArea); //目标窗口Handle
Refresh(windowHandle_UserPromotedNotificationArea); //刷新
//刷新溢出通知区域
IntPtr notifyIconOverflowWindowHandle = FindWindow("NotifyIconOverflowWindow", null); //父窗口Handle
string windowTile_OverflowNotificationArea = _CultureInfo.Name == "zh-CN" ? "溢出通知区域" : _CultureInfo.Name.StartsWith("en") ? "Overflow Notification Area" : null; //窗口标题,目前仅支持简体中文和英文
IntPtr windowHandle_OverflowNotificationArea = FindWindowEx(notifyIconOverflowWindowHandle, IntPtr.Zero, "ToolbarWindow32", windowTile_OverflowNotificationArea); //目标窗口Handle
Refresh(windowHandle_OverflowNotificationArea); //刷新
}
//刷新托盘区域中的所有图标(通过模拟鼠标滑过的方式)
private static void Refresh(IntPtr windowHandle)
{
//Mouse Over消息常量
const uint WM_MOUSEMOVE = 0x0200;
//获取托盘区域(位置信息放在Rect变量中)
GetClientRect(windowHandle, out Rect rect);
//通过循环,从左到右,从上到下,(模拟鼠标)滑过托盘区域中的每个图标
for (int x = 0; x < rect.right; x += 5) //图标之间的水平间距为5
{
for (int y = 0; y < rect.bottom; y += 5) //图标之间的垂直间距为5
{
SendMessage(windowHandle, WM_MOUSEMOVE, 0, (y << 16) + x); //发送鼠标滑过(Mouse Over)消息
}
}
}
}