Добавляем иконку в System Tray

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

Исходный текст был откомпилирован в Visual C++ 4.2.  Выбор данного компилятора ни чем не обоснован, просто мне удобно в нём работать, вот и всё.

А теперь по-порядку. Сразу представлю исходный код , а в конце подробно его прокомментирую.   Листинг 1 (status_ico.cpp)

#include <windows.h>
#include <windowsx.h>

LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL WndProc_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct);
void WndProc_OnDestroy(HWND hWnd);
void AddStatusIcon(HWND hWnd, DWORD dwMessage);
void HandlePopupMenu (HWND hwnd, POINT point);

#define WM_NOTIFYICONMSG (WM_USER + 2)

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
HWND hWnd;
WNDCLASSEX wndclass;

hInst = hInstance;

wndclass.style = 0;
wndclass.lpfnWndProc = (WNDPROC)WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInst;
wndclass.hIcon = NULL;
wndclass.hCursor = NULL;
wndclass.hbrBackground = NULL;
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = "WSCLAS";

if(!RegisterClassEx(&wndclass))
if(!RegisterClass((LPWNDCLASS)&wndclass.style)) return FALSE;

hWnd=CreateWindow("WSCLAS", "", 0, 0, 0, 1, 1, HWND_DESKTOP, NULL, hInst, NULL);

AddStatusIcon(hWnd, NIM_ADD);

while(GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return msg.wParam;
}

LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
POINT pt;

case WM_NOTIFYICONMSG:
switch(lParam) {
case WM_LBUTTONDBLCLK:
MessageBox(NULL, "Двойной щелчок по иконке", "Сообщение от иконки", MB_OK);
break;

case WM_RBUTTONDOWN: // нажатие на иконку правой кнопкой мыши
GetCursorPos(&pt); //вычисляем текущее положение курсора
HandlePopupMenu (hWnd, pt); //рисуем меню от координат курсора
break;

default:
break;
}
break;

HANDLE_MSG(hWnd, WM_CREATE, WndProc_OnCreate); //стандартный обработчик создания окна
HANDLE_MSG(hWnd, WM_DESTROY, WndProc_OnDestroy); //стандартный обработчик уничтожения окна

default:
return(DefWindowProc(hWnd, msg, wParam, lParam));
}
return 0;
}

BOOL WndProc_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct)
{
return TRUE;
}

void WndProc_OnDestroy(HWND hWnd)
{
PostQuitMessage(0);
}

void AddStatusIcon(HWND hWnd, DWORD dwMessage)
{
HICON hStatusIcon; // Хэндл иконки в статус-баре
LPCSTR pszIDStatusIcon; // Указатель на Иконку в статус-баре
NOTIFYICONDATA tnd;

pszIDStatusIcon = MAKEINTRESOURCE(IDI_ICON1);

hStatusIcon = LoadIcon( hInst,pszIDStatusIcon );
tnd.cbSize = sizeof(NOTIFYICONDATA);
tnd.hWnd = hWnd;
tnd.uID = 1;
tnd.uFlags = NIF_MESSAGE|NIF_ICON|NIF_TIP;
tnd.uCallbackMessage = WM_NOTIFYICONMSG;
tnd.hIcon = hStatusIcon;
lstrcpyn(tnd.szTip, "Пример", sizeof(tnd.szTip));
Shell_NotifyIcon( dwMessage, &tnd );
}

void HandlePopupMenu (HWND hWnd, POINT point)
{
HMENU hMenu;
HMENU hMenuTrackPopup;
LPCSTR pszIDMenu;

pszIDMenu = MAKEINTRESOURCE(IDM_POPUPMENU1);
hMenu = LoadMenu (hInst, pszIDMenu);
if (!hMenu) return;
hMenuTrackPopup = GetSubMenu (hMenu, 0);
TrackPopupMenu (hMenuTrackPopup, 0, point.x, point.y, 0, hWnd, NULL);
DestroyMenu (hMenu);
}
Комментарии:
В самом начале идёт обязательное включение Виндузовых заголовков (как и в любой программе под Windows. Далее я объявляю используемые в программе функции. Обычно такое объявление делается в файле-заголовке (.h) , но когда функций не так много, то я обычно эти описания вставляю в главный файл. Описание главной функции  WinMain не нужно добавлять.

Далее идёт определение идентификатора сообщения (#define WM_NOTIFYICONMSG (WM_USER + 2)), которое будет поступать от иконки. Впринципе этому идентификатору можно приствоить любое число, но чтобы быть уверенным, что такое число не используется каким-нибудь другим идентификатором, обычно присваивают  WM_USER + n   , где n - любое число.

Далее идёт основная функция, с которой начинает работать данная программа. Вообще-то в разных версиях API данная функция описывается по-разному. Например в API 16 её описание начинается с дескриптора PASCAL, но в API 32 - это WINAPI.

Следующим шагом программа создаёт окно, но не простое, а невидимое. То есть запущенная программа не будет видна в панели задач. Вообще процесс создания окна в Windows состоит из двух шагов. Первый ( RegisterClassEx(&wndclass) )- это регистрация класса окна в системе, и второй ( hWnd=CreateWindow(.... )- создание непосредственно окна. Параметр wndclass.lpfnWndProc = (WNDPROC)WndProc; указывает классу окна, что все сообщения, поступающие окну будут обрабатываться функцией WndProc. Все описания данных функций и их входных параметров Вы можете найти во многих книгах по программированию в API 32, поэтому здесь я не буду отвлекаться на эти вопросы.

Далее вызывается функция AddStatusIcon(hWnd, NIM_ADD) , задача которой заключается в том, чтобы нарисовать в панели статуса нужную иконку. Параметр hWnd означает, что все сообщения (...нажатия на ней мышкой...) от иконки будут передаваться главному окну нашей программы. Параметр NIM_ADD указывает функции, что иконка будет добавлена в статус-бар. Этот параметр может принимать значение NIM_MODIFY, означающий, что уже существующая иконка будет изменена или будет изменена её информационная надпись, которая появляется при навидении на иконку мышкой.

И наконец функция WinMain завершается бесконечным циклом, задача которого обрабатывать сообщения, поступающие главному окну. Естевственно этот цикл не такой уж и бесконечный, при поступлении определённых сообщений (например WM_CLOSE ) цикл может звершиться, и соответственно завершится сама программа. Например, если послать окну сообщение WM_DESTROY , то функция обработки оконных сообщений ( WndProc ) вызовет WndProc_OnDestroy , и наша программа завершит свою работу в системе. Соответственно при создании окна окно получает сообщение WM_CREATE и оконная функция вызывает WndProc_OnCreate(). Ещё можно добавить, что все неопознанные сообщения, или те, для которых Вы не предусмотрите обработчики сообщений напрвляются DefWindowProc(hWnd, msg, wParam, lParam) , вызов каторой находится в конце WndProc.