15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту

Данный пример представляет собой небольшую программку, при запуске которой, в строке статуса (там где в 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.