Иконки в «System Tray»
Практические ответы.
Автор: Nickolay Merkin
The RSDN Group
Опубликовано: 04.04.2002
Исправлено: 15.04.2009
Версия текста: 1.1.1
Библиотека ShellIcons (чистый WinAPI)
Пример использования: TrayTest (MFC)
Предисловие
Эта статья была задумана как ответы на многочисленные вопросы об иконках в System Tray (далее — трей):
- Как показать/изменить иконку (см. раздел «API»)
- Как вывести контекстное меню (см. раздел «Обработка событий»)
В результате тестовая программа выросла в библиотеку, упрощающую работу с иконками.
API для работы с иконками.
ПРИМЕЧАНИЕ
Здесь описана функциональность, общая для всех версий Windows (начиная с Win2000, появились дополнения — версия 5 библиотеки shellapi).
|
Описание
Shell_NotifyIcon
Для того, чтобы показать иконку в трее, используется функция Shell_NotifyIcon
BOOL Shell_NotifyIcon(
DWORD dwMessage,
NOTIFYICONDATA* lpData
);
|
Первый параметр (dwMessage)
- NIM_ADD – добавить иконку в трей
- NIM_DELETE – убрать
- NIM_MODIFY – обновить
В версии 5 появились еще две команды:
- NIM_SETFOCUS – установить фокус для ручного ввода на данную иконку
- NIM_SETVERSION – установить версию (включить режим совместимости с Win95)
Второй параметр – ссылка на структуру NOTIFYICONDATA
Структура NOTIFYICONDATA
Листинг 1. Объявление структуры NOTIFYICONDATA
typedef struct _NOTIFYICONDATA {
DWORD cbSize;
HWND hWnd;
UINT uID;
UINT uFlags;
UINT uCallbackMessage;
HICON hIcon;
#if (_WIN32_IE < 0x0500)
TCHAR szTip[64];
#else
TCHAR szTip[128];
#endif
#if (_WIN32_IE >= 0x0500)
DWORD dwState;
DWORD dwStateMask;
TCHAR szInfo[256];
union {
UINT uTimeout;
UINT uVersion;
} DUMMYUNIONNAME;
TCHAR szInfoTitle[64];
DWORD dwInfoFlags;
#endif
#if (_WIN32_IE >= 0x600)
GUID guidItem;
#endif
} NOTIFYICONDATA, *PNOTIFYICONDATA;
|
Первое поле – cbSize – служит для передачи размера структуры, как это принято в WinAPI:
NOTIFYICONDATA nid;
memset(&nid, 0, sizeof(nid));
nid.cbSize = sizeof(nid);
|
Следующие поля – 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])
|
lstrcpyn(nid.szTip, _T("Tool tip for my icon"), 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.
Применение
Добавление иконки
Инициализируем структуру.
NOTIFYICONDATA nid;
memset(&nid, 0, sizeof(nid)); nid.cbSize = sizeof(nid);
nid.hWnd = MyHWND;
nid.uID = 1234;
nid.hIcon = ::LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MyTrayIcon));
|
ПРЕДУПРЕЖДЕНИЕ
В дальнейшем, при удалении или изменении, не забудьте удалить hIcon (см. ниже).
|
Остальные поля заполняются по мере необходимости.
lstrcpyn(nid.szTip, "Это моя иконка", sizeof(nid.szTip)/sizeof(nid.szTip[0]));
|
Полем uFlags указываем, какие поля заполнены:
nid.uFlags = NIF_ICON | NIF_TIP;
|
Вызываем функцию:
BOOL bSuccess = Shell_NotifyIcon(NIM_ADD, &nid);
|
Удаление иконки
Инициализируем структуру, указав uID ранее созданной иконки, вызываем функцию:
BOOL bSuccess = Shell_NotifyIcon(NIM_DELETE, &nid);
|
ПРИМЕЧАНИЕ
Если окно закрыть, не удалив иконку вручную, то она остается жить до первого обращения (наведения мышки).
|
Если объект иконки более не нужен, уничтожим его:
Изменение иконки
Инициализируем структуру, устанавливая те поля, которые хотим изменить.
Можно менять изображение (как это делает The Bat!, машущий крыльями), текст подсказки (удаленное соединение – пишет, сколько байт передано), оконное сообщение (как правило, его устанавливают один раз при добавлении; в принципе, так можно включать/выключать обработку событий).
lstrcpyn(nid.szTip, "Новый текст", sizeof(nid.szTip)/sizeof(nid.szTip[0]));
|
Устанавливаем соответствующие флаги uFlags.
Вызываем функцию:
BOOL bSuccess = Shell_NotifyIcon(NIM_MODIFY, &nid);
|
Обработка событий
Трей ловит события от мыши (начиная с версии 5, от клавиатуры: вызов контекстного меню) и посылает их в окно с помощью PostMessage(hWnd, uCallbackMessage, (WPARAM)uID, (LPARAM)uMsg). То есть, если навести мышь на иконку, то WM_MOUSEMOVE будет передано вторым параметром (LPARAM).
ПРИМЕЧАНИЕ
Сопровождающая информация (координаты, состояния кнопок) не посылается и должна быть извлечена соответствующими функциями WinAPI (GetCursorPos).
|
Приложение может выбрать любой код для сообщения (uCallbackMessage), как правило, это или число WM_USER + …, WM_APP + …, либо зарегистрированное в системе с помощью RegisterWindowMessage().
Простой обработчик сообщений
(чистый WinAPI)
Листинг 2. Обработчик событий от иконки
#define WM_ShellNote (WM_APP + 1)
#define id_MyIcon 123
void AddMyIcon()
{
NOTIFYICONDATA nid;
memset(&nid, 0, sizeof(nid));
nid.cbSize = sizeof(nid);
nid.hWnd = hwnd_MyWindow;
nid.uID = id_MyIcon;
nid.hIcon = hicon_MyIcon;
nid.uCallbackMessage = WM_ShellNote;
nid.dwFlags = NIF_ICON|NIF_MESSAGE;
Shell_NotifyIcon(NIM_ADD, &nid);
}
LRESULT MyWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
...
if(uMsg == WM_ShellNote && hWnd == hwnd_MyWindow && wParam == id_MyIcon)
{
switch((UINT)lParam)
{
case WM_MOUSEMOVE: return OnIcon_MouseMove();
case WM_LBUTTONDOWN: return OnIcon_LeftButtonDown();
case WM_LBUTTONUP: return OnIcon_LeftButtonUp();
case WM_LBUTTONDBLCLK: return OnIcon_LeftButtonDoubleClick();
case WM_RBUTTONDOWN: return OnIcon_RightButtonDown();
case WM_RBUTTONUP: return OnIcon_RightButtonUp();
case WM_RBUTTONDBLCLK: return OnIcon_RightButtonDoubleClick();
case WM_CONTEXTMENU: return OnIcon_ContextMenu();
// для версии 5 и выше
}
}
...
}
|
Контекстное меню принято показывать в ответ на щелчок правой кнопкой, то есть на WM_RBUTTONDOWN - WM_RBUTTONUP.
Здесь есть тонкость: пользователь может нажать кнопку на иконке, затем увести мышь, отпустить кнопку, ... потом снова нажать, вернуть мышь, отпустить. Для иконки это будет выглядеть как медленный щелчок или небольшой драг-н-дроп. Обычный для других случаев выход из положения – захват мыши (::SetCapture) неприменим, поэтому все действия производятся по «переднему фронту»: WM_xBUTTONDOWN (одинарный щелчок), WM_xBUTTONDBLCLK (двойной щелчок).
Спецэффекты
Как показать контекстное меню
Делается это так же, как и в любом другом случае. Единственное отличие – в том, что координаты мыши не переданы в составе сообщения, и их придется брать напрямую.
Листинг 3. Вызов контекстного меню
BOOL ShowPopupMenu(HWND hWnd, HINSTANCE hInstance, WORD nResourceID)
{
HMENU hMenu = ::LoadMenu(hInstance,
MAKEINTRESOURCE(nResourceID));
if(!hMenu) return FALSE;
HMENU hPopup = ::GetSubMenu(hMenu, 0);
if(!hPopup) return FALSE;
SetForegroundWindow(hWnd);
POINT pt;
::GetCursorPos(&pt);
BOOL bOK = ::TrackPopupMenu(hPopup, 0, pt.x, pt.y, 0, hWnd, NULL);
::DestroyMenu(hMenu);
return bOK;
}
|
ПРЕДУПРЕЖДЕНИЕ
Если не вызвать SetForegroundWindow(hWnd), то меню не сможет автоматически закрытся по щелчку мыши за его пределами.
|
Как видите, ничего сложного нет.
Комментария требует только GetSubMenu().
В ресурсе хранятся «полосы меню» (menu bars), предназначенные для встраивания в окна. Если его показать функцией TrackPopupMenu, то мы увидим узкую вертикальную полоску без текста.
Поэтому нужно либо создавать «всплывающее меню» (popup menu) функцией CreatePopupMenu(), либо брать подменю (которое по определению является «всплывающим»). Соответственно, ресурс этого меню выглядит как полоса с одним элементом (номер 0), в подменю которого сложена вся функциональность.
Как свернуть окно в трей
Перефразируем задачу: как спрятать окно, убрав его кнопку с панели задач и показать иконку в трее?
Спрятать окно можно, вызвав ShowWindow сперва с параметром SW_MINIMIZE, а затем — SW_HIDE.
Восстановить — SW_SHOW (при этом оно появится в панели задач), а затем — SW_RESTORE (восстановить из свернутого состояния).
Когда пользователь командует «свернуть» (нажатие кнопки на заголовке окна, двойной щелчок по кнопке в панели задач, пункт системного меню), посылается сообщение WM_SIZE с параметром SIZE_MINIMIZED. Обработчик этого события может свернуть окно в трей.
Ниже приведен код на WinAPI. Перенос его на MFC или WTL — упражнение для читателя.
Листинг 4. Сворачивание-разворачивание в трей
#define WM_FLIPPED_TO_TRAY (WM_APP + 1234)
#define ID_FLIPPED_TO_TRAY 1234
BOOL FlipToTray(HWND hWnd, HICON hIcon, BOOL bMinimize)
{
NOTIFYICONDATA nid; memset(&nid, 0, sizeof(nid)); nid.cbSize = sizeof(nid);
nid.hWnd = hWnd;
nid.uID = ID_FLIPPED_TO_TRAY;
nid.uCallbackMessage = WM_FLIPPED_TO_TRAY;
nid.hIcon = hIcon;
GetWindowText(hWnd, nid.szToolTip,
sizeof(nid.szToolTip)/sizeof(nid.szToolTip[0]));
nid.dwFlags = NIF_ICON|NIF_MESSAGE|NIF_TOOLTIP;
BOOL ok = Shell_NotifyIcon(NIM_ADD, &nid);
if(bMinimize)
ShowWindow(hWnd, SW_MINIMIZE);
if(ok)
ShowWindow(hWnd, SW_HIDE);
return ok;
}
BOOL UnflipFromTray(HWND hWnd, BOOL bRestore)
{
NOTIFYICONDATA nid; memset(&nid, 0, sizeof(nid)); nid.cbSize = sizeof(nid);
nid.hWnd = hWnd;
nid.uID = ID_FLIPPED_TO_TRAY;
BOOL ok = Shell_NotifyIcon(NIM_DELETE, &nid);
if(!bRestore) return ok;
ShowWindow(hWnd, SW_SHOW);
ShowWindow(hWnd, SW_RESTORE);
return ok;
}
LRESULT CALLBACK MyWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
. . .
if(uMsg == WM_SIZE)
{
if(wParam == SIZE_MINIMIZED)
FlipToTray(hWnd, hTrayIcon, FALSE);
}
if(uMsg == WM_FLIPPED_TO_TRAY)
{
if(wParam == ID_FLIPPED_TO_TRAY && lParam == WM_LBUTTONDBLCLK)
UnflipFromTray(hWnd, TRUE);
}
. . .
}
|
Обертки для иконки
Задача оберткок — упростить работу с иконкой. Какую функциональность они могут взять на себя?
Во-первых, автоматизировать вызовы API
- Заполнение структуры NOTIFYICONDATA
- Прослойка для вызовов API
- Обновление иконки при изменении ее параметров
- Загрузка иконок из ресурсов
Во-вторых, избавление пользователя от необходимости прикреплять иконку к пользовательским окнам:
- Создание общего (скрытого) окна-носителя
- Централизованная регистрация иконок
В третьих, упрощение обработки событий
- Централизованная регистрация обработчиков событий от иконок
- Простой и гибкий синтаксис обработки
Наконец, автоматизация типовых действий
- Показ контекстного меню
- Сворачивание окна в трей
Пример такой обертки — библиотека ShellIcons (написанная мной).
Библиотека ShellIcons
ShellIcons написана на Visual C++ с без использования MFC и с минимальным использованием STL. Впрочем, из-за простоты ее легко перенести на любую технологию.
Исходные тексты: ShellIcons.zip
См. также 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.
Листинг 5. Объявление класса CShellIcon
class SHELLICONS_API CShellIcon
{
public:
CShellIcon();
virtual ~CShellIcon();
public:
HWND getHostWindow() const;
bool setHostWindow(HWND hWnd);
UINT getTrayID() const;
bool setTrayID(UINT uID);
UINT getCallbackMessage() const;
void setCallbackMessage(UINT uMsg);
HICON getIcon() const;
void setIcon(HICON hIcon, bool bOwn = true);
bool loadIcon(HINSTANCE hInstance, LPCTSTR sResourceID);
bool loadIcon(HINSTANCE hInstance, WORD wResourceID);
LPCTSTR getToolTip() const;
void setToolTip(LPCTSTR sText);
bool addIcon();
bool removeIcon();
bool modifyIcon();
bool update();
bool isAdded() const;
bool isIdentified() const;
bool isReady() const;
bool isModified() const;
bool getImmediate() const;
void setImmediate(bool bImmediate);
protected:
NOTIFYICONDATA m_nid;
bool m_bOwnIcon;
bool m_bImmediate;
bool m_bAdded;
void init();
};
|
Отличительная черта («фича») этого класса — способность отслеживать и моментально применять изменения свойств иконки, а также автоматически удалять ее из панели.
Кроме того, он автоматически уничтожает хэндл иконки (hIcon) при изменении (setIcon(), loadIcon() ) и в деструкторе.
Использование очень простое:
- Создать объект CShellIcon
- Передать ему все необходимые свойства иконки (обязательно — хэндл окна, идентификатор и хэндл иконки)
- Время жизни иконки в трее ограничено временем жизни объекта (и, естественно, окна).
Расширенные обертки (СExtShellIcon, CExtShellIconHost)
Листинг 6. Объявление класса CExtShellIcon
class CExtShellIcon;
class CExtShellIconHost;
struct _ShellIconNote
{
virtual LRESULT note(
CExtShellIconHost* pHost,
CExtShellIcon* pIcon,
UINT uMsg
) = 0;
};
typedef LRESULT (*PFNShellIconNote)
(CExtShellIconHost* pHost, CExtShellIcon* pIcon, UINT uMsg);
class SHELLICONS_API CExtShellIcon: protected CShellIcon
{
protected:
friend class CExtShellIconHost;
CExtShellIcon(CExtShellIconHost* pHost, UINT nTrayID);
virtual ~CExtShellIcon();
LRESULT Callback(UINT uMsg);
public:
inline CExtShellIconHost* getHost() const { return m_pHost; }
inline UINT getTrayID() const { return CShellIcon::getTrayID(); }
inline PFNShellIconNote getCallbackFn() const { return m_pfnCallback; }
inline void setCallbackFn(PFNShellIconNote pfn) { m_pfnCallback = pfn; }
inline _ShellIconNote* getCallbackItf() const { return m_pCallbackItf; }
inline void setCallbackItf(_ShellIconNote* pItf) { m_pCallbackItf = pItf; }
inline HICON getIcon() const
{ return CShellIcon::getIcon(); }
inline void setIcon(HICON hIcon, bool bOwn = true)
{ CShellIcon::setIcon(hIcon, bOwn); }
inline bool loadIcon(HINSTANCE hInstance, LPCTSTR sResourceID)
{ return CShellIcon::loadIcon(hInstance, sResourceID); }
inline bool loadIcon(HINSTANCE hInstance, WORD wResourceID)
{ return CShellIcon::loadIcon(hInstance, wResourceID); }
inline LPCTSTR getToolTip() const
{ return CShellIcon::getToolTip(); }
inline void setToolTip(LPCTSTR sText)
{ CShellIcon::setToolTip(sText); }
inline bool show(bool bShow)
{ return bShow ? CShellIcon::addIcon() : CShellIcon::removeIcon(); }
inline bool update()
{ return CShellIcon::update(); }
inline bool isReady() const
{ return CShellIcon::isReady(); }
inline bool isShown()
const { return CShellIcon::isAdded(); }
bool getImmediate() const { return CShellIcon::getImmediate(); }
void setImmediate(bool bImmediate) { CShellIcon::setImmediate(bImmediate); }
private:
CExtShellIconHost* m_pHost;
PFNShellIconNote m_pfnCallback;
_ShellIconNote* m_pCallbackItf;
};
|
ПРИМЕЧАНИЕ
CExtShellIcon наследует CShellIcon в защищенном режиме: базовый класс обладает избыточной функциональностью, ставящей под угрозу целостность системы (хэндл окна и сообщение нотификации поставляются исключительно носителем и не могут быть произвольно заменены).
|
Листинг 7. Объявление класса CExtShellIconHost
class SHELLICONS_API CExtShellIconHost
{
public:
CExtShellIconHost();
virtual ~CExtShellIconHost();
virtual HWND getHWnd() const = 0;
virtual void destroy() = 0;
UINT getFreeTrayID() const;
CExtShellIcon* addIcon(
UINT nID,
_ShellIconNote* pItf = NULL,
PFNShellIconNote pfn = NULL
);
CExtShellIcon* getIcon(UINT nID) const;
int countIcons() const;
bool removeIcon(UINT nID);
bool removeIcon(CExtShellIcon* pIcon);
int removeIcons(PFNShellIconNote pfn);
int removeIcons(_ShellIconNote* pItf);
void removeAllIcons();
protected:
#ifdef _SHELLICONS_USE_STL
typedef std::map<UINT, CExtShellIcon*> mapIcons;
mapIcons m_mapIcons;
#else
_MapUintToPtr* m_ptrMapIcons;
#endif
LRESULT notify(UINT nID, UINT uMsg);
};
|
CExtShellIconHost содержит коллекцию объектов CExtShellIcon.
ПРЕДУПРЕЖДЕНИЕ
Использование STL напрямую приводит к туче предупреждений компилятора VC++ (классы STL не объявлены как экспортируемые) и способно привести к ошибкам выделения памяти со статической библиотекой C RunTime.
|
С другой стороны, использование абстрактной коллекции (интерфейс которой см. ниже) делает код более громоздким.
Листинг 8. Интерфейс коллекции
class SHELLICONS_API _MapUintToPtr
{
public:
_MapUintToPtr() {}
virtual ~_MapUintToPtr() {}
virtual int count() = 0;
virtual bool add(UINT nID, LPVOID pValue) = 0;
virtual LPVOID take(UINT nID) = 0;
virtual LPVOID drop() = 0;
virtual LPVOID drop(UINT nID) = 0;
virtual bool find(LPVOID pValue, UINT& nID) = 0;
class _Enum
{
public:
_Enum() {}
virtual ~_Enum() {}
virtual bool end() = 0;
virtual UINT key() = 0;
virtual LPVOID value() = 0;
virtual bool next() = 0;
virtual bool remove() = 0;
};
virtual _Enum* start() = 0;
};
|
Реализация коллекции уже использует STL, но скрывает ее от клиента.
Реализация носителя: CWinShellIconHost
Листинг 9. Объявление класса CWinShellIconHost
class SHELLICONS_API CWinShellIconHost: public CExtShellIconHost
{
public:
CWinShellIconHost();
virtual ~CWinShellIconHost();
virtual HWND getHWnd() const { return m_hWnd; }
virtual void destroy();
bool CreateMe();
protected:
HWND m_hWnd;
static ATOM RegisterMyClass();
static LRESULT CALLBACK MyWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void onDestroy();
protected:
struct SINGLETON
{
CWinShellIconHost* pGlobal;
SINGLETON(): pGlobal(NULL) {}
~SINGLETON()
{
destroy();
}
void destroy()
{
if(!pGlobal)
return;
pGlobal->destroy();
delete pGlobal;
}
CWinShellIconHost* make()
{
if(pGlobal)
return pGlobal;
pGlobal = new CWinShellIconHost;
if(!pGlobal->CreateMe())
destroy();
return pGlobal;
}
};
static SINGLETON singleton;
public:
static CWinShellIconHost* Host() { return singleton.make(); }
};
|
Объект создает невидимое окно, которое поддерживает иконки (предоставляет хэндл окна, обрабатывает события).
ПРЕДУПРЕЖДЕНИЕ
Окно носителя пользуется помпой сообщений текущего потока.
|
Теоретически, можно создать сколько угодно носителей, но на практике удобно иметь один, т.е. синглетон.
Обработка событий
Как видно выше, пользователь может указать иконке либо функцию (PFNShellIconNote), либо интерфейс объекта-обработчика (_ShellIconNote). Приоритет отдается объектам.
В функцию (или метод) обработчика передается ссылка на носитель и на иконку, от которой пришло событие, а также код сообщения (uMsg) — WM_MOUSEMOVE и тому подобное.
Для того, чтобы не писать switch(uMsg), существует простая реализация класса-обработчика:
Листинг 10. Объявление класса CShellIconNote
class SHELLICONS_API CShellIconNote : public _ShellIconNote
{
public:
CShellIconNote();
virtual ~CShellIconNote();
virtual LRESULT note
(CExtShellIconHost* pHost, CExtShellIcon* pIcon, UINT uMsg);
virtual void onTrayMouseMove
(CExtShellIconHost* pHost, CExtShellIcon* pIcon);
virtual void onTrayLeftButtonDown
(CExtShellIconHost* pHost, CExtShellIcon* pIcon);
virtual void onTrayLeftButtonUp
(CExtShellIconHost* pHost, CExtShellIcon* pIcon);
virtual void onTrayLeftButtonDblClk
(CExtShellIconHost* pHost, CExtShellIcon* pIcon);
virtual void onTrayRightButtonDown
(CExtShellIconHost* pHost, CExtShellIcon* pIcon);
virtual void onTrayRightButtonUp
(CExtShellIconHost* pHost, CExtShellIcon* pIcon);
virtual void onTrayRightButtonDblClk
(CExtShellIconHost* pHost, CExtShellIcon* pIcon);
virtual void onTrayMouseMove (UINT nID);
virtual void onTrayLeftButtonDown (UINT nID);
virtual void onTrayLeftButtonUp (UINT nID);
virtual void onTrayLeftButtonDblClk (UINT nID);
virtual void onTrayRightButtonDown (UINT nID);
virtual void onTrayRightButtonUp (UINT nID);
virtual void onTrayRightButtonDblClk(UINT nID);
virtual void onTrayMouseMove ();
virtual void onTrayLeftButtonDown ();
virtual void onTrayLeftButtonUp ();
virtual void onTrayLeftButtonDblClk ();
virtual void onTrayRightButtonDown ();
virtual void onTrayRightButtonUp ();
virtual void onTrayRightButtonDblClk();
};
|
Он анализирует код сообщения и вызывает соответствующий метод.
Таким образом, для обработки, скажем, щелчка правой кнопкой (вызов контекстного меню) достаточно перекрыть единственный метод onTrayRightButtonDown().
Утилиты
Листинг 11. Объявление класса CTrackMenu
enum MENU_INDEX { MENU_LEFT, MENU_RIGHT, MENU__COUNT };
class SHELLICONS_API CTrackMenu
{
public:
CTrackMenu();
virtual ~CTrackMenu();
HWND getHWnd() const;
BOOL setHWnd(HWND hWnd);
HMENU getMenu(MENU_INDEX mi) const;
BOOL setMenu(MENU_INDEX mi, HMENU hMenu, BOOL bOwn = true);
BOOL loadMenu(MENU_INDEX mi, HINSTANCE hInstance, LPCTSTR sMenuResource);
BOOL loadMenu(MENU_INDEX mi, HINSTANCE hInstance, WORD wMenuResource);
BOOL trackMenu(MENU_INDEX mi, int x, int y);
BOOL trackMenu(MENU_INDEX mi, const POINT* ppt);
BOOL trackMenu(MENU_INDEX mi, const POINT& pt);
BOOL trackMenu(MENU_INDEX mi);
protected:
HWND m_hWnd;
HMENU m_hMenu[MENU__COUNT];
bool m_bOwn[MENU__COUNT];
bool m_bTracking;
};
|
Объект CTrackMenu позволяет загрузить несколько меню, правильным способом их показать, и разрушить по перезаписи или удалению объекта.
Листинг 12. Объявление класса CHideWindow
class SHELLICONS_API CHideWindow : protected CShellIconNote
{
public:
CHideWindow();
virtual ~CHideWindow();
HWND getHWnd() const;
BOOL setHWnd(HWND hWnd);
void hide();
bool update();
void show();
protected:
HWND m_hWnd;
UINT m_nIcon;
BOOL createIcon();
void removeIcon();
void onTrayRightButtonDown();
void onTrayLeftButtonDblClk();
};
|
CHideWindow умеет сворачивать окно в трей и восстанавливать его оттуда. Сворачивание — по инициативе клиента (окно перехватывает событие минимизации). Разворачивание — по двойному щелчку на иконке.
Иконка и текст подсказки берутся из окна. Если заголовок окна поменялся, достаточно вызвать метод update() для обновления иконки в трее.
Объект использует носитель иконок CWinShellIconHost, что позволило, во-первых, динамически выделять номера иконок, а во-вторых, не принуждать клиентов самостоятельно обрабатывать события нотификации.
Пример использования. Диалог с иконкой в трее.
Текст примера: TrayTest.zip
Это простое MFC-приложение, демонстрирующее все приемы, описанные выше.
- Добавление и модификация иконок
- Обработка событий
- Вывод контекстных меню
- Сворачивание в трей
- Локальные (CShellIcon) и централизованные (CWinShellIconHost) иконки
Вот, собственно, и все.
Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы
то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских
прав.