On-Line Библиотека www.XServer.ru - учебники, книги, статьи, документация, нормативная литература.
       Главная         В избранное         Контакты        Карта сайта   
    Навигация XServer.ru






 

Как попасть в System Tray?

Игорь Орещенков

Приходит время, и начинающему Windows-программисту наскучивает открывать и закрывать окно с лозунгом "Hello, World". Его взгляд рассеянно блуждает по "рабочему столу" (не по тому, на котором стоит монитор, а по тому, что создан бессмертной Microsoft) и останавливается на правом нижнем его углу. Та часть панели задач, что привлекла его внимание, именуется "system tray" и содержит значки, которые магическим образом взаимодействуют с программами, работа которых "на первый взгляд как будто не видна" (эти программы нельзя "вызвать" с помощью комбинации [Alt]+[Tab]).

Вышеупомянутого программиста начинает мучить вопрос: а чем его творение хуже Clock, dr.Web, AVP и прочих (посмотрите на свой system tray и продолжите список). Почему бы не растолкать корифеев и не занять свое место под солнцем (точнее, под курсором "мыши") в этом уютном уголке.
В этой статье приводятся сведения, достаточные для того, чтобы любой программист, знакомый с принципами разработки приложений под Windows 9x, мог разместить значок в system tray и управлять им.
Как уже было сказано, system tray - это часть панели задач, которая включает в себя еще меню кнопки "Пуск", кнопки выбора приложения и всплывающее меню. Официальное название system tray - область состояния (status area). Сюда приложения могут размещать значки, которые информируют пользователя об их состоянии или о возникновении событий.
Размещая в этой области значок принтера, приложение сообщает о начале процесса печати. Если курсор "мыши" замрет над каким-нибудь значком, всплывает информационная строка: "паук" dr.Web сообщает о количестве проверенных/инфицированных файлов, "часы" показывают дату, "системный монитор" - количество свободных ресурсов.
Кроме того, значки system tray позволяют организовать обратную связь с пользователем для приложений, окна которых по ряду причин нежелательно размещать на "рабочем столе". Например, чтобы вызвать окно настройки громкости, нужно дважды щелкнуть "мышью" по значку "громкоговорителя". В этом смысле значки выполняют функцию окна приложения: информируют пользователя и принимают сообщения от "мыши".
Для работы со значками в области состояния предназначена всего одна функция WIN32 API - Shell_NotifyIcon. Она описана следующим образом:
WINSHELLAPI BOOL WINAPI Shell_NotifyIcon(
DWORD dwMessage,
PNOTIFYICONDATA pnid);

При вызове параметр dwMessage должен содержать одно из следующих значений:
- NIM_ADD - добавить значок в область состояния,
- NIM_DEL - удалить значок из области состояния,
- NIM_MODIFY - изменить значок в области состояния.
Параметр pnid указывает на структуру типа NOTIFYICONDATA, значения полей которой зависят от параметра dwMessage.
Функция Shell_NotifyIcon возвращает ненулевое значение, если операция прошла успешно, и ноль в случае ошибки.
Рассмотрим подробнее структуру NOTIFYICONDATA. Она описана следующим образом:
typedef struct _NOTIFYICONDATA {
DWORD cbSize;
HWND hWnd;
UINT uID;
UINT uFlags;
UINT uCallbackMessage;
HICON hIcon;
char szTip[64];
} NOTIFYICONDATA, *PNOTIFYICONDATA;
и ее поля имеют следующий смысл:
- cbSize - размер структуры NOTIFYICONDATA,
- hWnd - манипулятор окна, которое будет получать сообщения от значка в области состояния;
- uID - идентификатор значка. Это значение передается приложению в качестве первого параметра (WPARAM) сообщения от значка;
- uFlags - набор флагов, которые определяют, какие поля структуры заданы корректно. Могут использоваться следующие значения или их комбинации с помощью логического "ИЛИ": NIF_ICON - поле hIcon корректно, NIF_MESSAGE - поле uCallbackMessage корректно, NIF_TIP - поле szTip корректно;
- uCallbackMessage - идентификатор сообщения, посылаемого окну hWnd при возникновении события "мыши" над значком в области состояния. Можно использовать значения WM_USER+N, где N - неотрицательное число;
- hIcon - манипулятор иконки, которую нужно разместить (изменить, удалить) в system tray;
- szTip - ASCIIZ-строка, которая будет использоваться в качестве "всплывающего" текста, когда указатель "мыши" остановится над значком. Если текст отсутствует, первый байт строки должен быть нулевым.
Перед вызовом функции Shell_NotifyIcon нужно подготовить экземпляр структуры NOTIFYICONDATA. Поля cbSize, hWnd и uID нужно заполнять всегда, остальные - по мере необходимости. В соответствии с заполнением полей uCallbackMessage, hIcon и szTip формируется поле флагов uFlags.
Чтобы добавить значок в область состояния, нужно вызвать функцию Shell_NotifyIcon, передав ей в качестве параметра dwMessage значение NIM_ADD, а в качестве pnid - указатель на инициализированный экземпляр структуры NOTIFYICONDATA. Если все выполнено правильно, функция вернет ненулевое значение, а в system tray появится новая иконка. Если планируется, что окно должно принимать сообщения от значка, следует обратить внимание, чтобы поле hWnd перед вызовом Shell_NotifyIcon было инициализировано значением манипулятора реально существующего окна. В противном случае значок будет исчезать из области состояния, как только над ним остановится указатель "мыши". Если было инициализировано поле uCallbackMessage, система будет посылать окну hWnd сообщения о событиях "мыши" над значком. При этом параметр сообщения WPARAM будет содержать идентификатор значка uID, а параметр LPARAM - тип сообщения.
Приложение, разместившее значок в system tray, может в любой момент изменить иконку или всплывающую подсказку. Для этого нужно внести изменения в соответствующие поля структуры NOTIFYICONDATA, поправить значение uFlags (значения cbSize, hWnd и uId изменяться не должны!) и вызвать функцию Shell_NotifyIcon со значением NIM_MODIFY в качестве параметра dwMessage.
Для удаления значка из system tray достаточно правильно заполнить поля cbSize, hWnd, uId и вызвать функцию Shell_NotifyIcon со значением параметра dwMessage = NIM_DELETE.
Приведенные сведения позволяют программисту жонглировать значками в system tray как только он пожелает. Дополнительную информацию и примеры использования функции Shell_NotifyIcon и обработки сообщений "мыши" можно найти в "Win32 Programmer's Reference", что поставляется в виде help-файла "Win32 online help" в составе Borland C++ 5.0.
Но если Вы программируете не на Ассемблере, а, к примеру, на C++, можно построить элегантный класс, который позволит еще больше упростить процесс работы со значками в области состояния.
Итак, за работу. Назовем наш класс SystemTrayIcon. Он должен содержать сведения о значке в области состояния и методы работы с ним. То есть он должен знать, как создать, модифицировать и удалить значок. Кроме того, в нем должна быть информация о том, размещен ли значок в system tray или еще нет.
Вся информация о значке содержится в структуре NOTIFYICONDATA, поэтому ее экземпляр можно поместить в класс, а чтобы защитить его от несанкционированного использования - ограничим доступ модификатором private. Туда же поместим логическую переменную fIsPlaced, которая будет принимать значение TRUE, если иконка размещена в system tray, и FALSE - в противном случае. Наш класс будет содержать следующие методы: конструктор (ну как же без него?), FAdd (размещает значок в system tray), FChangeIcon (изменяет иконку), FChangeTip (изменяет всплывающую подсказку), FDelete (удаляет значок из system tray) и деструктор. Запишем то, о чем сказано, в терминах C++:

class SystemTrayIcon {
		BOOL		fIsPlaced;
		NOTIFYICONDATA nid;
		void SetTip (LPSTR lpszTip);
public:
		SystemTrayIcon (): fIsPlaced (FALSE) {}
		~SystemTrayIcon ()
		{
			if (fIsPlaced)
				FDelete ();
		}
		BOOL FAdd (HWND hwnd, UINT uID, HICON hicon, LPSTR lpszTip, UINT msg);
		BOOL FChangeIcon (HICON hicon);
		BOOL FChangeTip (LPSTR lpszTip);
		BOOL FDelete ();
};

Что же у нас получилось? Для размещения значка в system tray нужно прежде всего создать объект класса SystemTrayIcon. Конструктор инициализирует класс и присваивает члену fIsPlaced значение FALSE, что соответствует отсутствию значка в system tray. Для добавления значка в область состояния нужно воспользоваться методом FAdd, передав ему в качестве параметров все необходимые сведения о значке (эти параметры соответствуют аналогичным параметрам функции Shell_NotifyIcon, поэтому здесь не расписываются). Этот метод, как и методы FChangeIcon, FChangeTip, FDelete, возвращает TRUE в случае удачи и FALSE - в противном случае. Реализация методов выглядит следующим образом:

void SystemTrayIcon::SetTip (LPSTR lpszTip)
{
	if (lpszTip)
		lstrcpyn (nid.szTip, lpszTip, sizeof (nid.szTip));
	else
		nid.szTip[0] = '\0';
}
BOOL SystemTrayIcon::FAdd (HWND hwnd, UINT uID, HICON hicon, LPSTR lpszTip, UINT msg)
{
	if (fIsPlaced)
		return FALSE;
	
	if (msg) {
		nid.cbSize = sizeof (NOTIFYICONDATA);
		nid.hWnd = hwnd;
		nid.uID = uID;
		nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
		nid.uCallbackMessage = msg;
		nid.hIcon = hicon;
		SetTip (lpszTip);
		fIsPlaced = Shell_NotifyIcon (NIM_ADD, &nid);
	}
	else
		fIsPlaced = FALSE;
	if (hicon)
		DestroyIcon (hicon);
	return fIsPlaced;
}
BOOL SystemTrayIcon::FChangeIcon (HICON icon)
{
	if (!fIsPlaced)
		return FALSE;
	HICON oldIcon = nid.hIcon;
	nid.hIcon = icon;
	nid.uFlags = NIF_ICON;
	BOOL res = Shell_NotifyIcon(NIM_MODIFY, &nid);
	if (!res)
		nid.hIcon = oldIcon;
	return res;
}
BOOL SystemTrayIcon::FChangeTip (LPSTR lpszTip)
{
	if (!fIsPlaced)
		return FALSE;
	char oldTip[sizeof (nid.szTip)];
	if (nid.szTip)
		lstrcpyn (oldTip, nid.szTip, sizeof (nid.szTip));
	else
		oldTip[0] = '\0';
	SetTip (lpszTip);
	nid.uFlags = NIF_TIP;
	BOOL res = Shell_NotifyIcon(NIM_MODIFY, &nid);
	if (!res)
		SetTip (oldTip);
	return res;
}
BOOL SystemTrayIcon::FDelete ()
{
	if (!fIsPlaced)
		return FALSE;
	fIsPlaced =!Shell_NotifyIcon(NIM_DELETE, &nid);
	return fIsPlaced;
}

Чтобы изменить иконку, просто обращаемся к методу FChangeIcon с манипулятором новой иконки в качестве параметра. Все необходимые значения в классе уже сохранены. Аналогичным образом с помощью метода FChangeTip изменяется всплывающая подсказка. Удалить значок из system tray можно с помощью метода FDelete или просто уничтожив объект класса SystemTrayIcon (тогда деструктор сделает это автоматически). 
Преимущества объектно-ориентированного подхода налицо. Ничто не мешает создать несколько объектов класса SystemTrayIcon и разместить в области состояния несколько значков. При дальнейшей работе с объектами нет необходимости задумываться над тем, какие поля инициализировать для изменения параметров значка или его удаления, нужно просто воспользоваться готовым методом. Удаление значков из system tray может выполняться автоматически при уничтожении соответствующего объекта.
Теперь можно подвести итоги. В статье рассмотрен способ размещения, удаления и работы со значками в system tray. Приведено описание функции Shell_NotifyIcon WIN32 API и объяснены ее параметры. В качестве примера построен С++ класс SystemTrayIcon, позволяющий упростить операции со значками в области состояния, и приведены реализации его методов. Надеюсь, что эта статья поможет кому-нибудь реализовать свои идеи в профессиональном воплощении и вдохновит на новые подвиги в области программирования под Windows. А ведь начиналось все с "Hello, World"!



Языки программирования: разное