В ответ на
публикацию вопроса Дмитрия о System Tray в
предыдущем выпуске помимо прямых ответов
пришло еще несколько просьб рассказать о
том, как в системный tray вообще помещать
иконки. Я, видимо, был излишне
оптимистичен, когда посчитал, что это все
знают ;) Так что я решил поведать
уважаемым читателям об этом в данном
выпуске, в рубрике "WINAPI", в расчете на
то, что эта информация будет полезна
многим. Получается, сегодняшний выпуск
целиком посвящен system tray ;)
Итак, задача у нас
следующая: поместить в системный tray свою
иконку, причем заставить ее
функционировать стандартным образом -
чтобы при наведении на нее появлялась
подсказка, при нажатии на правую кнопку
мыши выскакивало меню, на левую -
производилось какое-нибудь действие.
Начнем с начала -
нужно поместить иконку в tray. Сами вы это
вряд ли сделаете - да это и не нужно. За вас
это сделает Windows, вам нужно только сообщить
операционной системе о своем намерении. Для
этого служит функция Shell_NotifyIcon( ), которая
позволяет создавать, изменять и удалять
такие иконки.
Первый аргумент
этой функции это код операции, которую вам
нужно осуществить. Он имеет три возможных
значения - NIM_ADD, NIM_DELETE и NIM_MODIFY. В пояснениях,
по-моему, не нуждается. Второй параметр -
указатель на структуру NOTIFYICONDATA. Вот как эта
структура выглядит:
typedef struct _NOTIFYICONDATA {
DWORD cbSize; //
размер, обязательно указывать
HWND hWnd; // HWND для посылки уведомлений
UINT uID; // идентификатор иконки в tray, не
имеет отношения к ресурсам
UINT uFlags; // см.
ниже
UINT uCallbackMessage; //
посылается вашей функции окна
HICON hIcon; // дескриптор иконки
CHAR szTip[64]; //
строка с подсказкой
} NOTIFYICONDATA;
// uFlags
#define NIF_MESSAGE 0x1 // uCallbackMessage содержит информацию
#define NIF_ICON 0x2 // hIcon содержит информацию
#define NIF_TIP 0x4 // szTip содержит информацию
В принципе,
назначение каждого члена этой структуры
довольно прозрачно. Замечу только, что uID -
это не идентификатор ресурса иконки, как
можно было бы подумать, а вами определенный
идентификатор для tray icon вашего приложения.
Иконка, которую выводит в tray приложение,
может меняться в процессе работы, но этот
идентификатор остается постоянным.
Также вам нужно в uCallbackMessage записать
сообщение, которое вы хотите чтобы система
вам посылала в качестве уведомления о
событиях, происходящих с вашей иконкой. Для
этого в программе определите какое-нибудь
user-defined сообщение, например так: #define
WM_TRAYNOTIFY (WM_APP+100)
WM_APP используется как раз для того, чтобы
именно в таком виде и определять нужные вам
сообщения.
Теперь, предположим у вас подготовлена
иконка для tray: IDI_MYTRAYICON. Нам нужно ее
вывести в tray. Вот что мы делаем:
// уведомляющее
сообщение
#define WM_TRAYNOTIFY (WM_APP+100)
//
идентификатор иконки
#define ID_TRAYICON 1000
...
CString sNotifyTip = "Название
вашей программы или другая подсказка";
NOTIFYICONDATA nid;
memset (&nid, 0, sizeof(nid) ); // обнулять структуру перед
использованием - хорошая привычка
Этот код вставьте в функцию инициализации,
причем окно вашего приложения уже
должно быть создано, hWnd и hInstance должны быть
определены. hWnd вы получаете при создании
окна, а hInstance вам передают прямо в WinMain. Если
у вас MFC-приложение, поставьте вместо них
соответственно AfxGetMainWnd()->m_hWnd
и AfxGetApp()->m_hInstance.
Ну вот, иконку мы
вывели, и даже подсказка у нас выводится.
Для своевременного удаления иконки в
функцию, обрабатывающую выход из программы,
поставьте примерно такую же конструкцию, но
с NIM_DELETE:
(в структуре nid достаточно теперь
определить только cbSize, hWnd и uID).
Но иконка бесполезна, если она ничего не
делает. Давайте добавим немного
функциональности. Система посылает нам
сообщение WM_TRAYNOTIFY каждый раз, когда с
иконкой что-то происходит. Все, что мы
должны сделать - обработать это
сообщение и отреагировать должным образом.
Добавьте в программу обработчик события
WM_TRAYNOTIFY. В этом сообщении wParam - это ID иконки,
а lParam - код сообщения от мыши, например
WM_RBUTTONDOWN. Если у вас не MFC-приложение, просто
добавьте один case в функцию окна. Если же
вы имеете дело с MFC, то сделайте следующее: в
класс главного окна(диалога) добавьте
функцию afxmsg void OnTrayNotify ( WPARAM wParam, LPARAM lParam );
В карту сообщений класса добавьте
следующую строку: ON_MESSAGE ( WM_TRAYNOTIFY, OnTrayNotify )
Таким образом обрабатываются
пользовательские сообщения. Эта строка
свяжет наше сообщение WM_TRAYNOTIFY с функцией
его обработки OnTrayNotify( ).
В этой функции
проверяйте значение lParam и делайте то, что
вам нужно, например, выводите меню. Как
именно это делать - уже совсем другая
история...
Q. Не подскажете как
в tray выводить текст, как например сделаны часы в
windows?
- Dmitriy
A1.
Copy from ListSOFT от
18.07.2000
"...Если хочешь, чтобы рядом с системными часами
располагалась надпись, например, твое имя, то в HKEY_CURRENT_USER\ Control panel\
International\
в первые два параметра запиши его (не более 8 символов), а в третий запиши
"HH:mm:ss tt". Кстати, если изменить формат времени таким способом, то строка,
записанная в первые два параметра будет фигурировать во всех программах,
запрашивающих время, например, в Outlook Express в графе Отправлено и
Получено."
- Grigori
Zagarski
Я попробовал так
сделать - не получилось. У меня в этом
разделе вообще всего один параметр - "Locale".
Что-то автор напутал... Может, путь указан неправильно?
Хоть результат и отрицательный, я решил все же
на всякий случай
опубликовать этот ответ - может, тут действительно дело во мне (я
проверял в Windows 98SE), ведь на ListSOFT действительно была такая
публикация. А может, кто и подскажет, в чем дело.
A2. Я предлагаю набирать
текст из иконок, которые должны создаваться
динамически.
Подобный подход я видел в нескольких программах, где tray-иконки
используются для индикации уровня занятости CPU и т.п.
Пример вывода текста в tray приаттачен. Сам вывод делается в классе
CShellNotifyText. Тестовая программка организует в tray'е что-то типа таймера.
Может быть это не самый простой вариант, но пусть кто-нибудь предложит
лучше :))
Хочу высказать свое положительное мнение о рассылке. Единственное, что
смущает в свете последних известий от Microsoft: Кому будут нужны знания по
MFC, когда все начнут
программировать на Си-диез (C#)?
- Сергей Цивин
Пример я посмотрел,
он работает. Но,
к сожалению, у такого подхода есть один
очень существенный недостаток: если TaskBar в
высоту имеет больше одной полосы , никто не
гарантирует, что у вас не произойдет
переноса на самом неподходящем для
этого символе. Я сам смоделировал такую
ситуацию, это было сделать легко и
выглядело совершенно неприемлемо. Если кто-нибудь
знает вдруг, как эту дилемму разрешить -
пишите.
А насчет C# - вынужден повториться, он не
позиционируется как конкурент VC и MFC. Microsoft
полагает, что это Java-киллер. Так, в следующую
версии VisualStudio известный продукт Visual J++,
скорее всего, не войдет, а вместо него будет
сами догадайтесь что...