Способ обеспечить удаление иконки в системном трее ... гарантировано - PullRequest
8 голосов
/ 20 января 2009

Можно ли гарантировать, что значок на панели задач удален?

Чтобы добавить значок в системном трее, вы:

Shell_NotifyIcon(NIM_ADD, &m_tnd);

Чтобы удалить значок в системном трее, вы:

Shell_NotifyIcon(NIM_DELETE, &m_tnd);

Что я хочу знать: что делать, если у вас происходит сбой приложения? Значок остается в системном трее, пока вы не наведите курсор мыши. Есть ли способ гарантировать, что значок будет удален даже в случае сбоя приложения? Я бы предпочел не использовать структурированную обработку исключений по разным причинам.

Другой случай, который я хочу обработать, - это когда процесс завершается, но не обязательно завершается сбоем.

Ответы [ 8 ]

4 голосов
/ 21 января 2009

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

Просто проверьте сообщение WM_TASKBARCREATED и заново создайте значок.

2 голосов
/ 20 января 2009

Лично я бы использовал Vectored Exception Handler. Да, он основан на SEH, но вам не нужно разбираться со всем другим стеком, который может потребоваться для раскрутки.

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

Внешнее приложение не очень поможет, не так ли? Он тоже может разбиться или погибнуть.

2 голосов
/ 20 января 2009

У вас может быть отдельная, более простая (и, следовательно, предположительно более надежная) программа, которая контролирует ваше приложение. Эта программа может фактически запустить вашу программу и затем контролировать процесс. Да, это очень уродливое решение.

1 голос
/ 20 января 2009

Хм, вы всегда можете вызвать вызов процесса внешнего монитора SendMessage с сообщением WM_PAINT в окно системного трея (что нужно будет сделать в зависимости от класса окна). Это должно удалить значок, который больше не действителен.

0 голосов
/ 27 апреля 2015

Существует много способов обеспечить вызов Shell_NotifyIcon(NIM_DELETE, &m_tnd); в C ++ для случая разрушения приложения; использование оболочки RAII поверх используемой вами NOTIFYICONDATA сделает работу, например:

struct NID
{
    NID() : icon_data() { icon_data.cbSize = sizeof(icon_data); }
    ~NID() { Shell_NotifyIcon(NIM_DELETE, &icon_data); }
    void Show(HWND w) { icon_data.hWnd = w; Shell_NotifyIcon(NIM_ADD, &icon_data); }
    NOTIFYICONDATA icon_data;
};

Это упрощенная версия оболочки, но она проиллюстрирует основную идею: если вы создадите экземпляр NID в статическом хранилище, он будет инициализирован до вызова WinMain или main, и его деструктор будет вызывается при очистке программы, даже если эта очистка вызвана ненормальным завершением.

Итак, мы можем использовать этот NOTIFYICONDATA ресурс, заключенный в struct NID, следующим образом:

NID nid; // <--- automatic storage duration, cleared after WinMain return
         // even if it returns normal or abnormally

int CALLBACK WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
    try
    {
        // GetMessage(&message, NULL, 0, 0) loop ...
        // ...
        // use nid.icon_data as you please
    }
    catch (...)
    {
        // something bad happened...
    }

    return 0;
}

В приведенном выше примере вызывается ~NID(), когда программа завершает работу (после исключения или после закрытия программы), деструктор вызывает Shell_NotifyIcon(NIM_DELETE, &icon_data);, и значок удаляется из области уведомлений; этот код охватывает нормальное завершение и исключение исключения, вы можете прочитать больше об этой теме в этот хороший ответ от NPE :

Что касается случая kill the process , то простого способа сделать это не существует.

Я уже проверял, что функции std::atexit и std::at_quick_exit не вызываются после завершения работы программы через диспетчер задач, поэтому я предполагаю, что вы должны перехватить вызов завершения ... это кажется довольно сложной задачей, но объяснено в этом ответе из BSH :

Когда процесс завершается (не закрывается), на самом деле ничего нельзя сделать, если вы не начнете делать некоторые перехваты, либо путем перехвата TerminateProcess или NtTerminateProcess в диспетчере задач процесс

Надеюсь, это поможет (хотя ответ через 6 лет, смеется)

0 голосов
/ 24 ноября 2010

Непосредственно не решает вашу проблему, но это было очень полезно для меня:

Я хотел избежать путаницы в системном трее. Поэтому для меня было достаточно «обновить» панель уведомлений при запуске. Это было сложнее, чем я думал, но , следующий за , демонстрирует решение SendMessage, которое имитирует очистку при наведении мыши, при которой не требуется фактически перемещать курсор пользователя.

Обратите внимание, что на компьютерах с Windows 7 имя Notification Area должно быть заменено на User Promoted Notification Area.

0 голосов
/ 20 января 2009

Вы можете использовать SetUnhandledExceptionFilter , чтобы поймать сбой. Я обычно использую его для создания файла дампа сбоя, чтобы сбой можно было отладить, но нет никаких причин, по которым вы не можете так просто выполнить очистку, как удаление значков в трее.

0 голосов
/ 20 января 2009

Вы должны обработать выход приложений при сбое так или иначе, или значок не исчезнет.

Проверьте это, если это может быть какая-либо помощь: http://www.codeproject.com/KB/shell/ashsystray.aspx

...