Как стать автором
Обновить

Значок в области уведомлений на C++

Время на прочтение 5 мин
Количество просмотров 10K

Давайте вместе напишем небольшую утилиту на С++. Пусть наша программка следит за чем-нибудь в системе и показывает значок в области уведомлений, когда это что-то произошло. Например, проверка будет на наличие файла или подключенного диска. Нет файла — значок один, появился файл — значок изменился.
У меня такой значок следит за подключением зашифрованного диска. Может быть кому-то нужно следить за появлением файла протокола с ошибками или наличием подключения к сетевому диску.

Для разработки можно воспользоваться бесплатной Visual C++ Express Edition.
Выберите создание нового проекта Win32 Project и назовите проект «Tray». По кнопке «Next» перейдите к настройкам проекта и установите флажок «Empty Project».

Начнем с приветствия


Добавьте к проекту С++ файл (.cpp) и назовите его «Tray». Начнем с такой практически минимальной программы. Попробуйте ввести ее текст и запустить.

#include <windows.h>
#include <tchar.h>

// Главная функция
int APIENTRY _tWinMain(HINSTANCE instance, HINSTANCE, LPTSTR, int)
{
	MessageBox(0, TEXT("Привет"), TEXT(""), 0);
	return 0;
}

Вызов MessageBox() показывает приветствие, а затем программа завершает работу. Если у вас получилось, двигаемся дальше.

Главное окно


Нам окно в принципе не нужно — весть интерфейс пользователя у нас представляет значок. Но окно требуется для создания значка и обработки его сообщений.
Для создания окна нужно определить функцию обработки сообщений WndProc(), зарегистрировать класс окна в структуре WNDCLASSEX и собственно создать окно функцией CreateWindowEx().

#include <windows.h>
#include <tchar.h>

// Обработка сообщений
LRESULT CALLBACK WndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
		// Стандартная обработка сообщений
		case WM_DESTROY:
			PostQuitMessage(0);
			break;
		default:
			return DefWindowProc(window, message, wParam, lParam);
	}
	return 0;
}

// Главная функция
int APIENTRY _tWinMain(HINSTANCE instance, HINSTANCE, LPTSTR, int)
{
	// Регистрация класса окна
	WNDCLASSEX main = { 0 };
	main.cbSize = sizeof(WNDCLASSEX);
	main.hInstance = instance;
	main.lpszClassName = TEXT("Main");
	main.lpfnWndProc = WndProc;
	RegisterClassEx(&main);

	// Создание главного окна
    HWND window = CreateWindowEx(0, TEXT("Main"), NULL, 0, 0, 0, 0, 0, NULL, NULL, instance, NULL);
    
	MessageBox(0, TEXT("Привет"), TEXT(""), 0);
	return 0;
}

Главное окно у нас отображаться никогда не будет, поэтому все параметры заполнены по минимуму. Попробуйте запустить программу — в ее работе ничего не изменилось.

Вывод значка


Собственно вывод значка выполняет функция Shell_NotifyIcon(). Ей в качестве параметра нужна структура NOTIFYICONDATA, а в этой структуре должен быть дескриптор нашего окна.
Структуру NOTIFYICONDATA будем хранить в глобальной переменной Icon, т.к. она нам еще пригодится. Создавать значок будем до окна приветствия, а перед выходом из программы удалим его.

#include <windows.h>
#include <tchar.h>

// Глобальные переменные
NOTIFYICONDATA Icon = { 0 }; // Атрибуты значка

// Обработка сообщений
...


// Главная функция
int APIENTRY _tWinMain(HINSTANCE instance, HINSTANCE, LPTSTR, int)
{
	// Регистрация класса окна
	...

	// Создание главного окна
    	...
 
	// Создание значка
	Icon.cbSize = sizeof(NOTIFYICONDATA);
	Icon.hWnd = window;
	Icon.uVersion = NOTIFYICON_VERSION;
	Icon.uCallbackMessage = WM_USER;
	Icon.hIcon = LoadIcon(NULL, IDI_SHIELD);
	Icon.uFlags = NIF_MESSAGE | NIF_ICON;
	Shell_NotifyIcon(NIM_ADD, &Icon);

	MessageBox(0, TEXT("Привет"), TEXT(""), 0);
	
	// Удаление значка
	Shell_NotifyIcon(NIM_DELETE, &Icon);
	return 0;    
}

Теперь запускайте программу на выполнение. Наш значок появляется при запуске, а затем исчезает после нажания ОК в диалоговом окне. Как выглядит значок, указывается в параметре hIcon. Мы туда при помощи функции LoadIcon() помещаем стандартную иконку IDI_SHIELD.

Цикл обработки сообщений


Настало время убрать диалоговое окно приветствия. При запуске программы у нас будет только значок в области уведомлений. Выход из программы сделаем по щелчку правой кнопкой мыши по значку. Должно будет появиться окно с вопросом о завершении работы программы.
Обработку сообщения от значка о том, что нажата кнопка мыши, добавим в функцию обработки сообщений. А цикл обработки сообщений вставим туда, где было приветствие.

...

// Обработка сообщений
LRESULT CALLBACK WndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
		// Сообщение от значка
		case WM_USER:
			if (lParam == WM_RBUTTONDOWN)
				if (MessageBox(NULL, TEXT("Завершить работу?"), TEXT("Tray"), MB_YESNO) == IDYES)
					DestroyWindow(window);
			break;
		
		// Стандартная обработка сообщений
		...
	}
	return 0;
}

// Главная функция
int APIENTRY _tWinMain(HINSTANCE instance, HINSTANCE, LPTSTR, int)
{
	// Регистрация класса окна
	...

	// Создание главного окна
	...

	// Создание значка
	...

	// Цикл обработки сообщений
	MSG message;
	while (GetMessage(&message, NULL, 0, 0))
	{
		TranslateMessage(&message);
		DispatchMessage(&message);
	}
	
	// Удаление значка
	Shell_NotifyIcon(NIM_DELETE, &Icon);
	return 0;    
}

После запуска программа показывает только значок, ожидая нажатия кнопки мыши.

Проверка по таймеру


Теперь осталось сделать то, ради чего все и затевалось. Раз в секунду будет выполняться проверка на наличие файла. Если такой файл появится, значок должен измениться. Включение таймера выполняется функцией SetTimer(), для которой интервал времени должен быть задан в миллисекундах.
Сама функция проверки существования файла может быть реализована разными способами. Вот способ проверки через получение атрибутов.

/ Проверка существования файла
bool FileExists(PTSTR path)
{
	return GetFileAttributes(path) != INVALID_FILE_ATTRIBUTES;
}

Если у функции GetFileAttributes() не получается прочесть атрибуты файла по заданному пути, значит файла нет. В качестве пути можно указывать путь к файлу, и тогда мы проверим существование файла. А если задать путь к диску, тогда мы проверим наличие диска. Допустим, мы будем проверять наличие диска «P:».
Чтобы не перерисовывать значок каждый раз, в глобальной переменной сохраним предыдущее состояние, и будем вызывать Shell_NotifyIcon() с новой картинкой только если состояние изменилось. Вот полная версия программы.

#include <windows.h>
#include <tchar.h>

// Глобальные переменные
NOTIFYICONDATA Icon = { 0 }; // Атрибуты значка
bool State = false; // Текущее состояние

// Проверка существования файла
bool FileExists(PTSTR path)
{
	return GetFileAttributes(path) != INVALID_FILE_ATTRIBUTES;
}

// Обработка сообщений
LRESULT CALLBACK WndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
		// Проверка по таймеру
		case WM_TIMER:
		{
			bool check = FileExists(TEXT("P:\\"));
			if (State != check)
			{
				if (State)
					Icon.hIcon = LoadIcon(NULL, IDI_SHIELD);
				else
					Icon.hIcon = LoadIcon(NULL, IDI_WARNING);
				Icon.uFlags = NIF_ICON;
				Shell_NotifyIcon(NIM_MODIFY, &Icon);
				State = check;
			}
			break;
		}
			
		// Сообщение от значка
		case WM_USER:
			if (lParam == WM_RBUTTONDOWN)
				if (MessageBox(NULL, TEXT("Завершить работу?"), TEXT("Tray"), MB_YESNO) == IDYES)
					DestroyWindow(window);
			break;
		
		// Стандартная обработка сообщений
		case WM_DESTROY:
			PostQuitMessage(0);
			break;
		default:
			return DefWindowProc(window, message, wParam, lParam);
	}
	return 0;
}

// Главная функция
int APIENTRY _tWinMain(HINSTANCE instance, HINSTANCE, LPTSTR, int)
{
	// Регистрация класса окна
	WNDCLASSEX main = { 0 };
	main.cbSize = sizeof(WNDCLASSEX);
	main.hInstance = instance;
	main.lpszClassName = TEXT("Main");
	main.lpfnWndProc = WndProc;
	RegisterClassEx(&main);

	// Создание главного окна
    HWND window = CreateWindowEx(0, TEXT("Main"), NULL, 0, 0, 0, 0, 0, NULL, NULL, instance, NULL);

	// Создание значка
	Icon.cbSize = sizeof(NOTIFYICONDATA);
	Icon.hWnd = window;
	Icon.uVersion = NOTIFYICON_VERSION;
	Icon.uCallbackMessage = WM_USER;
	Icon.hIcon = LoadIcon(NULL, IDI_SHIELD);
	Icon.uFlags = NIF_MESSAGE | NIF_ICON;
	Shell_NotifyIcon(NIM_ADD, &Icon);

	// Включение таймера
	SetTimer(window, 0, 1000, NULL);

	// Цикл обработки сообщений
	MSG message;
	while (GetMessage(&message, NULL, 0, 0))
	{
		TranslateMessage(&message);
		DispatchMessage(&message);
	}
	
	// Удаление значка
	Shell_NotifyIcon(NIM_DELETE, &Icon);
	return 0;    
}

В конфигурации Release программа Tray.exe получилась размером 8 Кбайт.
Теги:
Хабы:
-10
Комментарии 17
Комментарии Комментарии 17

Публикации

Истории

Работа

QT разработчик
13 вакансий
Программист C++
122 вакансии

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн