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