以编程方式刷新系统托盘图标

我有一个有系统托盘图标的应用程序。 在卸载时,如果它正在运行,我将终止该进程。 因此,由于没有优雅地停止应用程序,图标仍保留在系统托盘中,只有当我们将鼠标hover在系统托盘上时才会删除。 我编写了一个代码,可以沿托盘运行光标并将光标放回原始位置。 这就是我所做的:

[DllImport("user32.dll")] static extern IntPtr FindWindow(string className, string windowName); [DllImport("user32.dll")] static extern IntPtr FindWindowEx(IntPtr parent, IntPtr child, string className, string windowName); [DllImport("user32.dll")] static extern bool GetWindowRect(HandleRef handle, out RECT rct); [StructLayout(LayoutKind.Sequential)] struct RECT { public int Left; public int Top; public int Right; public int Bottom; } void RefreshTray() { IntPtr taskbar_Handle = FindWindow("Shell_Traywnd", ""); IntPtr tray_Handle = FindWindowEx(taskbar_Handle, IntPtr.Zero, "TrayNotifyWnd", ""); RECT rct; if (!(GetWindowRect(new HandleRef(null, tray_Handle), out rct))) { } System.Drawing.Point init = Control.MousePosition; for (int i = rct.Left; i < rct.Right-20; i++) { Cursor.Position = new System.Drawing.Point(i, (rct.Bottom + rct.Top) / 2); } Cursor.Position = init; } 

这在所有情况下均有效,除非启用了“不显示通知图标”选项。 在这种情况下,我有什么方法可以刷新托盘吗?

编辑正如评论建议我改变了我的方法。 我没有杀死托盘应用程序,而是建立了我的应用程序服务之间的通信(是的,忘记提及,我有一个服务与应用程序一起运行)和托盘应用程序。 卸载时,我停止服务,从服务停止方法我将特定格式的套接字消息发送到托盘应用程序并要求它关闭,我会将通知图标可见性设置为false。 这将使托盘应用程序在后台运行,因此我使用“taskkill”删除应用程序。 它在Win7和Vista中运行良好,但在Win XP中无法正常工作。 但我没有编写任何特定于环境的代码。 任何可能的线索?

这与我使用的相似。 我添加到触摸库界面的简单浮动键盘。 用户还想将我的键盘作为其桌面上的独立应用程序。 所以我做了这个,为它创建了一个托盘应用程序。 现在 – 如果它打开并启动我的画廊怎么办?

他们会有两个键盘。

当然 – 用户可以结束第一个 – 但更容易结束它。 我杀了它没有任何反响,所以我这样做。 但托盘图标仍在等待活动。 为了解决这个问题,我刷新了Tray区域。

请注意 – 这只适用于英语语言环境安装。 要使其适用于其他语言,请将“用户升级通知区域”和“通知区域”更改为已翻译/等效字符串。

 [StructLayout(LayoutKind.Sequential)] public struct RECT { public int left; public int top; public int right; public int bottom; } [DllImport("user32.dll")] public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); [DllImport("user32.dll")] public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow); [DllImport("user32.dll")] public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect); [DllImport("user32.dll")] public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam); public static void RefreshTrayArea() { IntPtr systemTrayContainerHandle = FindWindow("Shell_TrayWnd", null); IntPtr systemTrayHandle = FindWindowEx(systemTrayContainerHandle, IntPtr.Zero, "TrayNotifyWnd", null); IntPtr sysPagerHandle = FindWindowEx(systemTrayHandle, IntPtr.Zero, "SysPager", null); IntPtr notificationAreaHandle = FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "Notification Area"); if (notificationAreaHandle == IntPtr.Zero) { notificationAreaHandle = FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "User Promoted Notification Area"); IntPtr notifyIconOverflowWindowHandle = FindWindow("NotifyIconOverflowWindow", null); IntPtr overflowNotificationAreaHandle = FindWindowEx(notifyIconOverflowWindowHandle, IntPtr.Zero, "ToolbarWindow32", "Overflow Notification Area"); RefreshTrayArea(overflowNotificationAreaHandle); } RefreshTrayArea(notificationAreaHandle); } private static void RefreshTrayArea(IntPtr windowHandle) { const uint wmMousemove = 0x0200; RECT rect; GetClientRect(windowHandle, out rect); for (var x = 0; x < rect.right; x += 5) for (var y = 0; y < rect.bottom; y += 5) SendMessage(windowHandle, wmMousemove, 0, (y << 16) + x); } 

使用此工具http://www.codeproject.com/Articles/19620/LP-TrayIconBuster

它遍历TrayNotifyWnd和NotifyIconOverflowWindow中的ToolBarButtons并删除具有空文件名的那些。

如果您不想这样做并且没有运行.NET4.0,那么使用管道或TCP之类的东西来关闭当前实例应该不难。

正如大家所暗示的那样,问题是通过终止你的进程,它没有机会取消注册其托盘图标实例,因此它会一直存在,直到Windows尝试向其发送事件(下次将鼠标移到它上面) )此时Windows将删除它。

根据您使用的安装程序,这可能非常简单或更加困难。 最流行的安装程序框架允许插件,其中一些支持管道,更多支持TCP请求。 或者,写一个安装程序可以在开始卸载过程之前运行的小型可执行文件,该文件与主应用程序通信并发送关闭消息。

最后请注意,如果您可以使用.NET4.0,那么我建议您查看内置的System.IO.Pipes命名空间和包含的类。