Обновление значков в системном трее программно - PullRequest
5 голосов
/ 01 декабря 2011

У меня есть приложение, которое имеет значок в системном трее. При удалении я убиваю процесс, если он запущен. Таким образом, поскольку приложение не корректно останавливается, значок остается в области уведомлений и удаляется только в том случае, если на нем навести курсор мыши. Я написал код, который будет запускать курсор вдоль панели и возвращать курсор в исходное положение. Вот что я сделал:

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

Это хорошо работает во всех случаях, кроме случаев, когда включена опция «не показывать значки уведомлений». Можно ли как-нибудь обновить лоток в этом случае?

EDIT Поскольку комментарии предложили, я изменил свой подход. Вместо того, чтобы убивать приложение в трее, я установил связь между службой приложений (да, забыл упомянуть, у меня тоже работает служба вместе с приложением) и приложением в трее. При удалении я останавливаю службу, из метода остановки службы я отправляю сообщение сокета определенного формата в приложение в трее и запрашиваю его закрытие, и я устанавливаю для значка уведомления видимость false. Это приведет к тому, что приложение Tray будет работать в фоновом режиме, поэтому я использую «taskkill» для удаления приложения. Он работал нормально в Win7 и Vista, но не работает должным образом в Win XP. Но я не написал никакого кода для конкретной среды. Любая возможная подсказка?

Ответы [ 4 ]

9 голосов
/ 29 февраля 2012

Это похоже на то, что я использую.Простая плавающая клавиатура, которую я добавил в интерфейс сенсорной галереи.Пользователь хотел также иметь мою клавиатуру в качестве отдельного приложения на своем рабочем столе.Итак, я сделал это, создал приложение для него.Теперь - что если он откроется, и они запустят мою галерею?

У них будет две клавиатуры.

Конечно, пользователь может завершить первую - но проще ее просто завершить.Нет никаких последствий от того, что я убил его, так что я делаю.Но значок в трее остается, так как его ждет событие.Чтобы обойти это, я обновляю область «Лоток».

Обратите внимание - Это будет работать только при установке на английском языке.Чтобы заставить его работать на другом языке, измените «Область уведомлений, продвигаемых пользователями», и «Область уведомлений» на переведенную / эквивалентную строку.

[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);
}
2 голосов
/ 10 июля 2013

Используйте этот инструмент http://www.codeproject.com/Articles/19620/LP-TrayIconBuster

Он перебирает ToolBarButtons в TrayNotifyWnd & NotifyIconOverflowWindow и удаляет файлы с пустыми именами файлов.

1 голос
/ 19 января 2012

Не должно быть сложно закрыть текущий экземпляр, используя что-то вроде каналов или TCP, если вы не хотите этого делать и не запускаете .NET4.0.

Как и все подразумевают,проблема в том, что, убивая ваш процесс, он не получает возможности отменить регистрацию своего экземпляра иконки в трее, поэтому он остается на месте, пока Windows не попытается отправить ему событие (в следующий раз, когда вы наведете на него курсор мыши), в какой момент Windowsудалит его.

В зависимости от того, какой установщик вы используете, это может быть довольно легко или более сложно.Самые популярные фреймворки для инсталляторов позволяют использовать плагины, и некоторые из них поддерживают Pipes, многие другие поддерживают запросы TCP.В качестве альтернативы, напишите небольшой исполняемый файл, который может запустить ваш установщик, прежде чем он начнет процесс удаления, который связывается с вашим основным приложением и отправляет сообщение о закрытии.

В качестве заключительного замечания, если вы можете использовать .NET4.0тогда я бы посоветовал взглянуть на встроенное пространство имен System.IO.Pipes и включенные классы.

0 голосов
/ 13 апреля 2014

Я обнаружил, что это решение (http://maruf -dotnetdeveloper.blogspot.com / 2012/08 / c-refreshing-system-tray-icon.html ) работает для меня.

...