What is the windows system tray icon

Иконки в «System Tray»


Практические ответы.


Автор: Nickolay Merkin
The RSDN Group

Опубликовано: 04.04.2002
Исправлено: 15.04.2009
Версия текста: 1.1.1

Предисловие

Эта статья была задумана как ответы на многочисленные вопросы об иконках в System Tray (далее — трей):

  • Как показать/изменить иконку (см. раздел «API»)
  • Как вывести контекстное меню (см. раздел «Обработка событий»)

В результате тестовая программа выросла в библиотеку, упрощающую работу с иконками.

API для работы с иконками.

Здесь описана функциональность, общая для всех версий Windows (начиная с Win2000, появились дополнения — версия 5 библиотеки shellapi).

Описание


Shell_NotifyIcon

Для того, чтобы показать иконку в трее, используется функция Shell_NotifyIcon

Первый параметр ( dwMessage )

  • NIM_ADD – добавить иконку в трей
  • NIM_DELETE – убрать
  • NIM_MODIFY – обновить

В версии 5 появились еще две команды:

  • NIM_SETFOCUS – установить фокус для ручного ввода на данную иконку
  • NIM_SETVERSION – установить версию (включить режим совместимости с Win95)

Второй параметр – ссылка на структуру NOTIFYICONDATA

Структура NOTIFYICONDATA

Первое поле – cbSize – служит для передачи размера структуры, как это принято в WinAPI:

Следующие поля – hWnd и uID – служат для идентификации иконки. Причем hWnd соответствует окну приложения (как правило, главному), а uID может быть произвольным и служит для различения иконок. Если включено сообщение для нотификации (uCallbackMessage, см. ниже), то оно будет посылаться этому окну.

Поле uFlags показывает, какие именно из остальных полей содержат информацию (комбинация битовых флагов):

  • NIF_ICON – иконка
  • NIF_MESSAGE – оконное сообщение
  • NIF_TIP – подсказка (tool tip)
  • NIF_STATE – состояние (начиная с версии 5)
  • NIF_INFO – расширенная подсказка (tool tip), которая появляется в «воздушном шарике» (balloon)
  • NIF_GUID – зарезервировано для версии 6.

Поле uCallbackMessage – номер оконного сообщения, которое будет посылаться окну hWnd при событиях от мышки и клавиатуры. (Подробнее об этом – в разделе «обработка событий»).

Поле hIcon – хэндл иконки.

СОВЕТ

В трее показываются иконки размером 16*16.

Ресурс иконки может включать несколько изображений разных размеров. Позаботьтесь добавить изображение 16*16 в ваш ресурс.

Поле szTip – строка подсказки размером до 64 (в версии 5 – до 128) символов.

ПРЕДУПРЕЖДЕНИЕ

Строка подсказки имеет тип TCHAR[], поэтому для работы с ней нужно использовать переносимые функции, а главное, правильно указывать размер в символах, а не байтах: sizeof(nid.szTip) / sizeof(nid.szTip[0])

Поля dwState и dwStateMask – управляют состоянием иконки (битовые флаги):

  • NIS_HIDDEN – скрыть иконку
  • NIS_SHARED – сделать иконку разделяемой ( кстати, кто знает, что бы это значило? )

Поля szInfo и szInfoTitle – большая подсказка в «воздушном шарике» (balloon). Аналогичны szTip.

Поле dwInfoFlags – стиль «воздушного шарика». Принимает одно из значений:

  • NIIF_ERROR – показать иконку Error (аналогично MessageBox)
  • NIIF_INFO – иконку Information
  • NIIF_NONE – без иконки
  • NIIF_WARNING – иконка Warning
  • NIIF_ICON_MASK – зарезервировано для версии 6
  • NIIF_NOSOUND – зарезервировано для версии 6

Поле uTimeout устанавливает задержку (в миллисекундах) для вывода «воздушного шарика». Допустимый диапазон – от 10.000 до 30.000 миллисекунд.

Поле uVersion служит для эмуляции функциональности предыдущих версий Shell. Оно принимает значения

  • 0 – Windows 95
  • NOTIFYICON_VERSION – текущая версия (начиная с Windows 2000).

ПРИМЕЧАНИЕ

Обратите внимание, что uTimeout и uVersion конкурируют. uVersion используется только при вызове Shell_NotifyIcon с параметром NIM_SETVERSION.

Поле guidItem зарезервировано для версии 6.

Применение


Добавление иконки

В дальнейшем, при удалении или изменении, не забудьте удалить hIcon (см. ниже).

Остальные поля заполняются по мере необходимости.

Полем uFlags указываем, какие поля заполнены:

Удаление иконки

Инициализируем структуру, указав uID ранее созданной иконки, вызываем функцию:

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

Если объект иконки более не нужен, уничтожим его:

Изменение иконки

Инициализируем структуру, устанавливая те поля, которые хотим изменить.

Можно менять изображение (как это делает The Bat!, машущий крыльями), текст подсказки (удаленное соединение – пишет, сколько байт передано), оконное сообщение (как правило, его устанавливают один раз при добавлении; в принципе, так можно включать/выключать обработку событий).

Устанавливаем соответствующие флаги uFlags.

Обработка событий

Трей ловит события от мыши (начиная с версии 5, от клавиатуры: вызов контекстного меню) и посылает их в окно с помощью PostMessage(hWnd, uCallbackMessage, (WPARAM)uID, (LPARAM)uMsg). То есть, если навести мышь на иконку, то WM_MOUSEMOVE будет передано вторым параметром (LPARAM).

ПРИМЕЧАНИЕ

Сопровождающая информация (координаты, состояния кнопок) не посылается и должна быть извлечена соответствующими функциями WinAPI (GetCursorPos).

Приложение может выбрать любой код для сообщения (uCallbackMessage), как правило, это или число WM_USER + …, WM_APP + …, либо зарегистрированное в системе с помощью RegisterWindowMessage().

Простой обработчик сообщений

Контекстное меню принято показывать в ответ на щелчок правой кнопкой, то есть на WM_RBUTTONDOWN — WM_RBUTTONUP.

Здесь есть тонкость: пользователь может нажать кнопку на иконке, затем увести мышь, отпустить кнопку, . потом снова нажать, вернуть мышь, отпустить. Для иконки это будет выглядеть как медленный щелчок или небольшой драг-н-дроп. Обычный для других случаев выход из положения – захват мыши (::SetCapture) неприменим, поэтому все действия производятся по «переднему фронту»: WM_xBUTTONDOWN (одинарный щелчок), WM_xBUTTONDBLCLK (двойной щелчок).

Спецэффекты


Как показать контекстное меню

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

Если не вызвать SetForegroundWindow(hWnd), то меню не сможет автоматически закрытся по щелчку мыши за его пределами.

Как видите, ничего сложного нет.

Комментария требует только GetSubMenu().

В ресурсе хранятся «полосы меню» (menu bars), предназначенные для встраивания в окна. Если его показать функцией TrackPopupMenu, то мы увидим узкую вертикальную полоску без текста.

Поэтому нужно либо создавать «всплывающее меню» (popup menu) функцией CreatePopupMenu(), либо брать подменю (которое по определению является «всплывающим»). Соответственно, ресурс этого меню выглядит как полоса с одним элементом (номер 0), в подменю которого сложена вся функциональность.

Как свернуть окно в трей

Перефразируем задачу: как спрятать окно, убрав его кнопку с панели задач и показать иконку в трее?

СОВЕТ

Вы можете воспользоваться программой TrayIt (http://www.teamcti.com/TrayIt), которая умеет сворачивать в трей окна любых приложений.

Спрятать окно можно, вызвав ShowWindow сперва с параметром SW_MINIMIZE, а затем — SW_HIDE.

Восстановить — SW_SHOW (при этом оно появится в панели задач), а затем — SW_RESTORE (восстановить из свернутого состояния).

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

Ниже приведен код на WinAPI. Перенос его на MFC или WTL — упражнение для читателя.

Обертки для иконки

Задача оберткок — упростить работу с иконкой. Какую функциональность они могут взять на себя?

Во-первых, автоматизировать вызовы API

  • Заполнение структуры NOTIFYICONDATA
  • Прослойка для вызовов API
  • Обновление иконки при изменении ее параметров
  • Загрузка иконок из ресурсов

Во-вторых, избавление пользователя от необходимости прикреплять иконку к пользовательским окнам:

  • Создание общего (скрытого) окна-носителя
  • Централизованная регистрация иконок

В третьих, упрощение обработки событий

  • Централизованная регистрация обработчиков событий от иконок
  • Простой и гибкий синтаксис обработки

Наконец, автоматизация типовых действий

  • Показ контекстного меню
  • Сворачивание окна в трей

Пример такой обертки — библиотека ShellIcons (написанная мной).

Библиотека ShellIcons

ShellIcons написана на Visual C++ с без использования MFC и с минимальным использованием STL. Впрочем, из-за простоты ее легко перенести на любую технологию.

См. также http://www.rsdn.ru//article/files/Classes/ni.xml — класс CNotifyIcon от Игоря Вартанова, аналогичный CShellIcon.

Библиотека представляет классы

  • CShellIcon — обертка структуры NOTIFYICONDATA
  • CExtShellIcon : protected CShellIcon — обертка иконки, прикрепленной к специальному носителю.
  • CExtShellIconHost — прототип носителя иконок, абстрагированный от окна
  • CWinShellIconHost — носитель иконок на окне, созданном средствами чистого WinAPI
  • _ShellIconNote — интерфейс обработчика событий
  • CShellIconNote — простая реализация обработчика с виртуальными методами «на все случаи жизни»

ПРИМЕЧАНИЕ

Абстрагирование носителя от окна было сделано, потому что «песочница» использовала MFC, и первая реализация носителя была MFC-шная.

CShellIcon

В чистом виде обертка структуры NOTIFYICONDATA.

Отличительная черта («фича») этого класса — способность отслеживать и моментально применять изменения свойств иконки, а также автоматически удалять ее из панели.

Кроме того, он автоматически уничтожает хэндл иконки (hIcon) при изменении (setIcon(), loadIcon() ) и в деструкторе.

Использование очень простое:

  • Создать объект CShellIcon
  • Передать ему все необходимые свойства иконки (обязательно — хэндл окна, идентификатор и хэндл иконки)
  • Время жизни иконки в трее ограничено временем жизни объекта (и, естественно, окна).

Расширенные обертки (СExtShellIcon, CExtShellIconHost)

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

CExtShellIconHost содержит коллекцию объектов CExtShellIcon.

ПРЕДУПРЕЖДЕНИЕ

Использование STL напрямую приводит к туче предупреждений компилятора VC++ (классы STL не объявлены как экспортируемые) и способно привести к ошибкам выделения памяти со статической библиотекой C RunTime.

С другой стороны, использование абстрактной коллекции (интерфейс которой см. ниже) делает код более громоздким.

Реализация коллекции уже использует STL, но скрывает ее от клиента.

Реализация носителя: CWinShellIconHost

Объект создает невидимое окно, которое поддерживает иконки (предоставляет хэндл окна, обрабатывает события).

ПРЕДУПРЕЖДЕНИЕ

Окно носителя пользуется помпой сообщений текущего потока.

Теоретически, можно создать сколько угодно носителей, но на практике удобно иметь один, т.е. синглетон.

Обработка событий

Как видно выше, пользователь может указать иконке либо функцию (PFNShellIconNote), либо интерфейс объекта-обработчика (_ShellIconNote). Приоритет отдается объектам.

В функцию (или метод) обработчика передается ссылка на носитель и на иконку, от которой пришло событие, а также код сообщения (uMsg) — WM_MOUSEMOVE и тому подобное.

Для того, чтобы не писать switch(uMsg), существует простая реализация класса-обработчика:

Он анализирует код сообщения и вызывает соответствующий метод.

Таким образом, для обработки, скажем, щелчка правой кнопкой (вызов контекстного меню) достаточно перекрыть единственный метод onTrayRightButtonDown().

Утилиты

Объект CTrackMenu позволяет загрузить несколько меню, правильным способом их показать, и разрушить по перезаписи или удалению объекта.

CHideWindow умеет сворачивать окно в трей и восстанавливать его оттуда. Сворачивание — по инициативе клиента (окно перехватывает событие минимизации). Разворачивание — по двойному щелчку на иконке.

Иконка и текст подсказки берутся из окна. Если заголовок окна поменялся, достаточно вызвать метод update() для обновления иконки в трее.

Объект использует носитель иконок CWinShellIconHost, что позволило, во-первых, динамически выделять номера иконок, а во-вторых, не принуждать клиентов самостоятельно обрабатывать события нотификации.

Пример использования. Диалог с иконкой в трее.

Это простое MFC-приложение, демонстрирующее все приемы, описанные выше.

  • Добавление и модификация иконок
  • Обработка событий
  • Вывод контекстных меню
  • Сворачивание в трей
  • Локальные (CShellIcon) и централизованные (CWinShellIconHost) иконки

Qt Documentation

Contents

The System Tray Icon example shows how to add an icon with a menu and popup messages to a desktop environment’s system tray.

Screenshot of the System Tray Icon

Modern operating systems usually provide a special area on the desktop, called the system tray or notification area, where long-running applications can display icons and short messages.

This example consists of one single class, Window , providing the main application window (i.e., an editor for the system tray icon) and the associated icon.

The editor allows the user to choose the preferred icon as well as set the balloon message’s type and duration. The user can also edit the message’s title and body. Finally, the editor provides a checkbox controlling whether the icon is actually shown in the system tray, or not.

Window Class Definition

The Window class inherits QWidget:

We implement several private slots to respond to user interaction. The other private functions are only convenience functions provided to simplify the constructor.

The tray icon is an instance of the QSystemTrayIcon class. To check whether a system tray is present on the user’s desktop, call the static QSystemTrayIcon::isSystemTrayAvailable() function. Associated with the icon, we provide a menu containing the typical minimize, maximize, restore and quit actions. We reimplement the QWidget::setVisible() function to update the tray icon’s menu whenever the editor’s appearance changes, e.g., when maximizing or minimizing the main application window.

Finally, we reimplement QWidget’s closeEvent() function to be able to inform the user (when closing the editor window) that the program will keep running in the system tray until the user chooses the Quit entry in the icon’s context menu.

Window Class Implementation

When constructing the editor widget, we first create the various editor elements before we create the actual system tray icon:

We ensure that the application responds to user input by connecting most of the editor’s input widgets (including the system tray icon) to the application’s private slots. But note the visibility checkbox; its toggled() signal is connected to the icon‘s setVisible() function instead.

The setIcon() slot is triggered whenever the current index in the icon combobox changes, i.e., whenever the user chooses another icon in the editor. Note that it is also called when the user activates the tray icon with the left mouse button, triggering the icon’s activated() signal. We will come back to this signal shortly.

The QSystemTrayIcon::setIcon() function sets the icon property that holds the actual system tray icon. On Windows, the system tray icon size is 16×16; on X11, the preferred size is 22×22. The icon will be scaled to the appropriate size as necessary.

Note that on X11, due to a limitation in the system tray specification, mouse clicks on transparent areas in the icon are propagated to the system tray. If this behavior is unacceptable, we suggest using an icon with no transparency.

Whenever the user activates the system tray icon, it emits its activated() signal passing the triggering reason as parameter. QSystemTrayIcon provides the ActivationReason enum to describe how the icon was activated.

In the constructor, we connected our icon’s activated() signal to our custom iconActivated() slot: If the user has clicked the icon using the left mouse button, this function changes the icon image by incrementing the icon combobox’s current index, triggering the setIcon() slot as mentioned above. If the user activates the icon using the middle mouse button, it calls the custom showMessage() slot:

When the showMessage() slot is triggered, we first retrieve the message icon depending on the currently chosen message type. The QSystemTrayIcon::MessageIcon enum describes the icon that is shown when a balloon message is displayed. Then we call QSystemTrayIcon’s showMessage() function to show the message with the title, body, and icon for the time specified in milliseconds.

macOS users note: The Growl notification system must be installed for QSystemTrayIcon::showMessage() to display messages.

QSystemTrayIcon also has the corresponding, messageClicked() signal, which is emitted when the user clicks a message displayed by showMessage().

In the constructor, we connected the messageClicked() signal to our custom messageClicked() slot that simply displays a message using the QMessageBox class.

QMessageBox provides a modal dialog with a short message, an icon, and buttons laid out depending on the current style. It supports four severity levels: «Question», «Information», «Warning» and «Critical». The easiest way to pop up a message box in Qt is to call one of the associated static functions, e.g., QMessageBox::information().

As we mentioned earlier, we reimplement a couple of QWidget’s virtual functions:

Our reimplementation of the QWidget::setVisible() function updates the tray icon’s menu whenever the editor’s appearance changes, e.g., when maximizing or minimizing the main application window, before calling the base class implementation.

We have reimplemented the QWidget::closeEvent() event handler to receive widget close events, showing the above message to the users when they are closing the editor window. On macOS we need to avoid showing the message and accepting the close event when the user really intends to quit the application, that is, when the user has triggered «Quit» in the menu bar or pressed the Command+Q shortcut.

In addition to the functions and slots discussed above, we have also implemented several convenience functions to simplify the constructor: createIconGroupBox() , createMessageGroupBox() , createActions() and createTrayIcon() . See the desktop/systray/window.cpp file for details.

В© 2021 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.

Читайте также:  Net send windows 10 примеры
Оцените статью