刷新系统托盘图标编程
本文关键字:编程 系统托盘图标 刷新 | 更新日期: 2023-09-27 18:16:34
我有一个有系统托盘图标的应用程序。在卸载时,我正在杀死正在运行的进程。所以,当我没有优雅地停止应用程序时,图标仍然在系统托盘中,只有当我们将鼠标悬停在它上面时才会移除。我编写了一段代码,使光标沿着托盘运行,并使光标回到初始位置。这是我所做的:
[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中运行良好,但在winxp中不能正常工作。但是我没有编写任何特定于环境的代码。有什么线索吗?
这与我使用的类似。一个简单的浮动键盘,我添加到触摸库界面。用户还想把我的键盘作为一个独立的应用程序放在他们的桌面上。我做了这个,为它创建了一个托盘应用。现在,如果我的画廊开张了怎么办?
他们会有两个键盘。
当然——用户可以结束第一个——但是直接结束它更容易。我杀了它不会有什么后果,所以我杀了它。但是托盘图标仍然存在,因为它在等待一个事件。为了解决这个问题,我刷新了Tray区域。
请注意 -这只适用于英语语言环境安装。要让它在另一种语言上工作,将"用户推广通知区域"answers"通知区域"更改为翻译后的/等效字符串。
[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);
}
使用管道或TCP之类的东西关闭当前实例应该不难,如果您不想这样做并且没有运行。net 4.0。
正如每个人都暗示的那样,问题是通过杀死你的进程,它没有机会注销它的托盘图标实例,所以它一直存在,直到Windows试图向它发送一个事件(下次你将鼠标移到它上面时),此时Windows将删除它。
根据您使用的安装程序,这可能相当容易或更困难。大多数流行的安装框架都允许插件,其中一些支持pipe,更多的支持TCP请求。或者,编写一个小的可执行文件,你的安装程序可以在开始卸载过程之前运行,它会与你的主应用程序通信并发送关闭消息。
最后一点,如果你可以使用。net 4.0,那么我建议你看看内置的System.IO.Pipes命名空间和包含的类。
使用这个工具http://www.codeproject.com/Articles/19620/LP-TrayIconBuster
它迭代traynotifywind &NotifyIconOverflowWindow并删除那些空文件名。
我发现这个(http://maruf-dotnetdeveloper.blogspot.com/2012/08/c-refreshing-system-tray-icon.html)解决方案为我工作。