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