Приветствую читателя этой статьи. Я студент, учусь по направлению "Приборостроение", но большую часть времени занимаюсь программированием. Все таки это меня привлекает больше. Задумывался по поводу смены ОС на Arch Linux, но пока отложил эту затею в долгий ящик. Смотрел различные ролики на YouTube и заметил, что многие пользователи ставят себе Polybar, в котором можно легко настраивать информацию, выводимую на нечто похожее на Панель задач в Windows. Тогда я подумал "А почему бы не сделать такое в винде?!" и сразу начал гуглить что к чему. Попытался найти готовые аналоги, но ничего не впечатлило, поэтому решил написать свою программу на C++.
Заранее хочу предупредить, это моя первая статья, поэтому было бы неплохо получить достаточно аргументированной критики. Может я что-то пропустил или недостаточно понятно объяснил, жду фидбека.
Выглядеть оно будет примерно так:

При наведении на иконку ОЗУ мы увидим загрузку памяти в GB и %, а на иконку CPU - загрузку процессора в процентах. Цвет иконку cpu будет меняться в зависимости от загрузки:
Зеленый - загрузка <25%
Оранжевый - загрузка 25-50%
Красный - >50%
Приступаем к разработке
Для начала создадим оконное приложение в VS и удалим все лишнее оттуда из .cpp и .rc файлов.
Подготовим иконки и добавим их в папку проекта.


Эту иконку я нашел на сайте -> https://www.flaticon.com/ru/free-icons/prosessor. Там же можно найти и иконку RAM. Они там в черном цвете, поэтому можно в обычной Paint залить их в нужный цвет, а через онлайн ресурсы конвертировать в .ico. Иконки можно подобрать любые, которые вам нравятся
В файле .rc (ресурсов) добавляем иконки следующим образом, чтобы их не пришлось тянуть рядом с нашим готовым exeшником.
IDR_CPUICON1 ICON "cpu4.ico"
IDR_CPUICON2 ICON "cpu6.ico"
IDR_CPUICON3 ICON "cpu5.ico"
IDR_RAMICON0 ICON "ram0.ico"
А в файле Recource.h добавляем им idшники#define IDR_CPUICON1 131
#define IDR_CPUICON2 132
#define IDR_CPUICON3 133
#define IDR_RAMICON0 134
Создадим 2 структуры, которые как раз таки и отвечают за иконку программы в таск баре:
NOTIFYICONDATA cpu_status_icon;
NOTIFYICONDATA ram_status_icon;
Подробнее о структуре -> https://learn.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-notifyicondataa
И 2 хендлера таймеров, которые будут работать в параллельных потоках, таким образом вывод информации будет независимый:
HANDLE htimer_ram = NULL;
HANDLE htimer_cpu = NULL;
void CALLBACK TimerRAM(PVOID pVoid, BOOLEAN TimerOrWaitFired)
{
static wchar_t ram_info[128];
if (GlobalMemoryStatusEx(&mem_info)) {
static uint16_t totalPhys = (uint16_t)ceil((float)mem_info.ullTotalPhys / 1024 / 1024 / 1024);
float physUsed = (float)(mem_info.ullTotalPhys - mem_info.ullAvailPhys) / 1024 / 1024 / 1024;
uint16_t usagePercent = mem_info.dwMemoryLoad;
swprintf(ram_info, 128, L"%.2f\\%u Gb %u%%", physUsed, totalPhys, usagePercent);
lstrcpy(ram_status_icon.szTip, ram_info);
Shell_NotifyIcon(NIM_MODIFY, &ram_status_icon);
}
}
void CALLBACK TimerCPU(PVOID pVoid, BOOLEAN TimerOrWaitFired)
{
static wchar_t cpu_inf[8];
double cpu_total = GetCPULoad();
if (cpu_total < 25)
cpu_status_icon.hIcon = cpu_icons[0];
else if (cpu_total < 50)
cpu_status_icon.hIcon = cpu_icons[1];
else if (cpu_total >= 50)
cpu_status_icon.hIcon = cpu_icons[2];
swprintf(cpu_inf, 8, L"%.2f %%", cpu_total);
lstrcpy(cpu_status_icon.szTip, cpu_inf);
Shell_NotifyIcon(NIM_MODIFY, &cpu_status_icon);
}
Обновлять информацию об ОЗУ будет сразу в таймере. Функция загрузки проца выглядит следующим образом:
float GetCPULoad() {
static FILETIME idleTimePrev = {}, kernelTimePrev = {}, userTimePrev = {};
FILETIME idleTime, kernelTime, userTime;
if (!GetSystemTimes(&idleTime, &kernelTime, &userTime)) return 0.0;
auto toUInt64 = [](FILETIME ft) {
return ((uint64_t)ft.dwHighDateTime << 32) | ft.dwLowDateTime;
};
uint64_t idleDiff = toUInt64(idleTime) - toUInt64(idleTimePrev);
uint64_t kernelDiff = toUInt64(kernelTime) - toUInt64(kernelTimePrev);
uint64_t userDiff = toUInt64(userTime) - toUInt64(userTimePrev);
idleTimePrev = idleTime;
kernelTimePrev = kernelTime;
userTimePrev = userTime;
uint64_t total = kernelDiff + userDiff;
return total ? (1.0 - (float)idleDiff / total) * 100.0 : 0.0;
}
Затем инициализируем все необходимые переменные, таймеры и т.п.:
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Сохранить маркер экземпляра в глобальной переменной
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
cpu_icons[0] = (HICON)LoadImage(hInstance, MAKEINTRESOURCE(IDR_CPUICON1), IMAGE_ICON, 64, 64, 0);
cpu_icons[1] = (HICON)LoadImage(hInstance, MAKEINTRESOURCE(IDR_CPUICON2), IMAGE_ICON, 64, 64, 0);
cpu_icons[2] = (HICON)LoadImage(hInstance, MAKEINTRESOURCE(IDR_CPUICON3), IMAGE_ICON, 64, 64, 0);
cpu_status_icon.cbSize = sizeof(NOTIFYICONDATA);
cpu_status_icon.hWnd = hWnd;
cpu_status_icon.uID = 1;
cpu_status_icon.hIcon = cpu_icons[0];
lstrcpy(cpu_status_icon.szTip, L"%");
cpu_status_icon.uFlags = NIF_ICON | NIF_TIP;
Shell_NotifyIcon(NIM_ADD, &cpu_status_icon);
mem_info.dwLength = sizeof(mem_info);
ram_status_icon.cbSize = sizeof(NOTIFYICONDATA);
ram_status_icon.hWnd = hWnd;
ram_status_icon.uID = 2;
ram_status_icon.hIcon = (HICON)LoadImage(hInstance, MAKEINTRESOURCE(IDR_RAMICON0), IMAGE_ICON, 64, 64, 0);;
lstrcpy(ram_status_icon.szTip, L"%");
ram_status_icon.uFlags = NIF_ICON | NIF_TIP;
Shell_NotifyIcon(NIM_ADD, &ram_status_icon);
if (!CreateTimerQueueTimer(&htimer_cpu, NULL, TimerCPU, NULL, 1000, 1500, WT_EXECUTEDEFAULT))
{
std::cerr << "Error cpu timer\n";
return -1;
}
if (!CreateTimerQueueTimer(&htimer_ram, NULL, TimerRAM, NULL, 1000, 1500, WT_EXECUTEDEFAULT))
{
std::cerr << "Error ram timer\n";
return -2;
}
return TRUE;
}
Не забываем удалить таймеры при завершении работы:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
DeleteTimerQueueTimer(NULL, htimer_cpu, NULL);
DeleteTimerQueueTimer(NULL, htimer_ram, NULL);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}