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

发布于 2024-12-18 18:22:47 字数 1430 浏览 1 评论 0原文

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

[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 中无法正常运行。但我还没有编写任何特定于环境的代码。有什么可能的线索吗?

I've an application which has a system tray icon. While uninstalling I'm killing the process if its running. So, as am not gracefully stopping the app, the icon remains in the system tray and will remove only if we hover the mouse on it. I wrote a code that would run the cursor along the tray and get the cursor back in its initial position. This is what I have done:

[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;
}

This works good in all the cases except when the option "do not show notification icons" is enabled. Is there some way I could refresh the tray in this case?

EDIT:

As the comments suggested I changed my approach. Instead of killing the tray application, I established a communication between my application service (yeah, forgot to mention, I have a service too running along with the application) and tray application. While uninstalling, I stop the service, from the service stop method I would send a socket message of a particular format to the tray application and ask it to close, and I would set the notify icon visibility to false. This would leave the Tray Application running in background, so I am using taskkill to remove the application. It worked fine in Win7 and Vista but is not working properly in Win XP. But I have not written any environment specific code. Any possible clue?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

柠北森屋 2024-12-25 18:22:47

这和我用的很相似。
我将一个简单的浮动键盘添加到触摸库界面。用户还希望将我的键盘作为桌面上的独立应用程序。所以我这样做了,为它创建了一个托盘应用程序。现在 - 如果它开放并且他们推出我的画廊怎么办?

他们会有两个键盘。

当然 - 用户可以结束第一个 - 但结束它更容易。我杀掉它不会有任何后果,所以我就这么做了。但托盘图标仍然存在,因为它正在等待事件。为了解决这个问题,我刷新了托盘区域。

请注意 - 这仅适用于英语区域设置安装。要使其适用于其他语言,请将“用户推广通知区域”和“通知区域”更改为翻译/等效字符串。

[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);
}

That's similar to what I use.
A simple floating Keyboard I added to a touch gallery interface. The user wanted to also have my keyboard as a standalone application on their desktop. So I did this, created a tray app for it. Now - what if its open and they launch my gallery?

They would have two keyboards.

Sure - the user could end the first - but its easier to just end it. There are no repercussions from me killing it, so I do. But the tray Icon remains, as its waiting for an event. To get around this, I refresh the Tray area.

Please note - This would only work on an English Locale Installation. To get this to work on another language, change "User Promoted Notification Area", and "Notification Area" to the translated / equivalent string.

[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);
}
若无相欠,怎会相见 2024-12-25 18:22:47

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

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

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

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

Shouldn't be difficult to close the current instance using something like pipes, or TCP if you don't feel like doing that and aren't running .NET4.0.

As everyone is implying, the issue is that by killing your process it doesn't get a chance to unregister its tray icon instance, so it sticks around until Windows attempts to send an event to it (the next time you move the mouse over it) at which point Windows will remove it.

Depending on what installer you are using, this could be quite easy or more difficult. Most popular installer frameworks allow for plugins, and a few of them support Pipes, many more support TCP requests. Alternatively, write up a small executable that your installer can run before it begins the uninstall process, which communicates with your primary app and sends a close message.

As a final note, if you can use .NET4.0 then I'd suggest looking at the built in System.IO.Pipes namespace and the included classes.

清醇 2024-12-25 18:22:47

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

它迭代 TrayNotifyWnd 和 ToolBarButtons 中的 ToolBarButtons。 NotifyIconOverflowWindow 并删除那些文件名为空的文件。

Use this tool
http://www.codeproject.com/Articles/19620/LP-TrayIconBuster

It iterates through ToolBarButtons in TrayNotifyWnd & NotifyIconOverflowWindow and removes those with null file names.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文