DotNetSurfers

Latish Sehgal's Blog

Refreshing the Windows System Tray Programmatically

I have been working on a project where I need to kill a process programmatically. Unfortunately, that leaves behind the orphaned icon for that process in the system tray (also known as the taskbar notification area). The icon goes away when you hover your mouse over it, but in interest of providing a better User Experience,  I have been trying to find a way to make the orphaned icon go away programmatically. Windows does not expose any API to refresh the system tray area, so the next best working solution I have been able to come up (thanks to this article) involves sending the Mouse Move Windows Message over the system tray area. It involves calling unmanaged Windows API and is not very pretty, but like I said, it’s the best solution I have been able to come up with. If you know of a better way to achieve this, please do leave a comment. Also this works for me on 64 bit Windows 7, but I think it should be compatible with other versions of Windows. The code is below and you just need to call TaskBarUtil.RefreshNotificationArea() from your client.

using System;

using System.Runtime.InteropServices;

using Microsoft.VisualStudio.OLE.Interop;

internal class TaskBarUtil
{
    public static void RefreshNotificationArea()
    {
        var notificationAreaHandle = GetNotificationAreaHandle();  
        if (notificationAreaHandle == IntPtr.Zero)
            return;  
        RefreshWindow(notificationAreaHandle);
    }  
    private static void RefreshWindow(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);
    }  
    private static IntPtr GetNotificationAreaHandle()
    {
        const string notificationAreaTitle = "Notification Area";
        const string notificationAreaTitleInWindows7 = "User Promoted Notification Area";  
        var systemTrayContainerHandle = FindWindowEx(IntPtr.Zero, IntPtr.Zero,
                                                    "Shell_TrayWnd", string.Empty);
        var systemTrayHandle = FindWindowEx(systemTrayContainerHandle, IntPtr.Zero,
                                                    "TrayNotifyWnd", string.Empty);
        var sysPagerHandle = FindWindowEx(systemTrayHandle, IntPtr.Zero, "SysPager", 
                                                    string.Empty);
        var notificationAreaHandle = FindWindowEx(sysPagerHandle, IntPtr.Zero, 
                                                    "ToolbarWindow32", 
                                                    notificationAreaTitle);  
        if (notificationAreaHandle == IntPtr.Zero)
            notificationAreaHandle = FindWindowEx(sysPagerHandle, IntPtr.Zero, 
                                                    "ToolbarWindow32",
                                                    notificationAreaTitleInWindows7);
        return notificationAreaHandle;
    }  
    [DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, 
                                                    string className, 
                                                    string windowTitle);  
    [DllImport("user32.dll")]
    static extern bool GetClientRect(IntPtr handle, out RECT rect);  
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    static extern IntPtr SendMessage(IntPtr handle, UInt32 message, Int32 wParam, 
                                                    Int32 lParam);
}

Comments