开发者

Tray icon does not disappear on killing process

I have a window service for my application. When i stops that by killing process with task manager, the tray icon does not disappear. Is it a开发者_如何学Python window bug or something else? Do we have a solution for that problem? Thanks in advance :).


You can let the icon disappear by calling the Dispose()-method of the specified NotifyIcon-object. In most cases these Container-object isn't part of the tree of components in your application so it will not disappear by killing the proces. When the user moves over the icon, the icon doesn't find it parent so it dissapears. But by calling the Dispose-method, it disapeared at least in my applications. So:

//creating a NotifyIcon
NotifyIcon notifyicon = new NotifyIcon();
notifyicon.Text = "Text"; 
notifyicon.Visible = true; 
notifyicon.Icon = new Icon(GetType(),"Icon.ico");
//let it disappear
notifyicon.Dispose();


There is no solution to this problem. If you kill process with task manager, it does not receive termination notification, and hence can not remove its icon from the tray. Try avoiding killing process this way. You can use net start/stop to kill a service or services.msc GUI.


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.


Move your mouse over the icon, and it will disappear. I've noticed this behavior in all versions of windows, including Win 7.


I often notice that too, with various applications. The death of the application is only noticed when you move the mouse over the icon.

I think the "bug" is with Windows, not your application. (I'm reluctant to call it a "bug", per se, because it was probably a conscious decision to leave this in. Explorer could check whether applications that registered icons are still running, but that might be too expensive.)


If an application is forcefully terminated (e.g. through Task Manager), then Windows does not remove the notification icon. Windows Explorer doesn't even notice that the application has gone away until it attempts to send a message (usually a mouse movement message) to the window that owns the notification icon. At that point, Windows will remove the now dead icon from the notification area.

Given that you can't intercept TerminateProcess, there's nothing that your program can do about this by itself.

I guess that Windows Explorer could watch for the owner window being destroyed (as when the application quits unexpectedly), but it doesn't.

Even if the application is shut down gracefully, it must still remember to remove any of its notification icons. That is: if you don't call Shell_NotifyIcon(NIM_DELETE) (the equivalent of NotifyIcon.Dispose) when your application shuts down (even gracefully), the icon will remain there until the mouse moves over it.

Oh, and if this is a service process that's displaying the notification icon, be aware that session 0 isolation in Windows Vista and Windows 7 will break it.


You can move your mouse over it, or you can send a WM_MOUSEMOVE message to it.

Here is some sample code (tested on Windows 10):

    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        public int Left, Top, Right, Bottom;
    }

    [DllImport("user32.dll")]
    static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);

    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr hWndChildAfter, string className, string? windowTitle);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, nuint wParam, nint lParam);

    const int WM_MOUSEMOVE = 0x0200;

    public static void RefreshTraybar()
    {
        RefreshHiddenTraybar();
        RefreshTraybarInTaskbar();
    }

    static void RefreshHiddenTraybar()
    {
        var hiddenTrayWnd = FindWindowEx(IntPtr.Zero, IntPtr.Zero, "NotifyIconOverflowWindow", null);
        var hiddenNotificationArea = FindWindowEx(hiddenTrayWnd, IntPtr.Zero, "ToolbarWindow32", null);
        RefreshArea(hiddenNotificationArea);
    }

    static void RefreshTraybarInTaskbar()
    {
        var trayInTaskbarWnd = FindWindowEx(IntPtr.Zero, IntPtr.Zero, "Shell_TrayWnd", null);
        var trayNotifyWnd = FindWindowEx(trayInTaskbarWnd, IntPtr.Zero, "TrayNotifyWnd", null);
        var sysPager = FindWindowEx(trayNotifyWnd, IntPtr.Zero, "SysPager", null);
        var trayNotificationArea = FindWindowEx(sysPager, IntPtr.Zero, "ToolbarWindow32", null);
        RefreshArea(trayNotificationArea);
    }

    static void RefreshArea(IntPtr area)
    {
        if (!GetClientRect(area, out var clientRect)) return;
            
        for (int x = 0; x < clientRect.Right; x += 10)
            for (int y = 0; y < clientRect.Bottom; y += 10)
                SendMessage(area, WM_MOUSEMOVE, 0, (y << 16) + x);
    }


I've done it by handling the ThreadException event and disposing the tray icon in that event handler.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜