Всё началось с того что я увидел утилиту от IBM/Lenovo для показа заряда батареи ноутбука в непривычном месте — в таскбаре/супербаре, но не в виде значка, а как панель(аналогичные используются для управления проигрывателями iTunes, WMP, Zune):
Т.к. у меня ноутбуки другого производителя, а искать как выцарапать эту софтину у производителя мне было лень — я начал искать аналог, и, к преглубокому удивлению, ничего не нашел! (если я не прав — покажите носом, буду весьма благодарен!)
Именно так я решил написать своё решение. Писать будем на C++. Я писал в Visual Studio 2010, можно использовать предыдущие версии. Главное — наличие установленного Windows SDK(ставится отдельно от студии, доступен бесплатно, скачать можно например тут)
Вот что у меня получилось:
Больше всего времени у меня занял поиск в интернете названия собственно того куда я хотел запихать отображение данных. Оно называется DeskBand, и что самое хорошее — в Windows SDK есть пример работы его! (у меня он по адресу C:\Program Files\Microsoft SDKs\Windows\v7.0\Samples\winui\shell\shellextensibility\deskbands).
Далее дело стояло за малым — надо найти как получать информацию о заряде батареи. Поисковик в сочетании с MSDN навёл меня на полезную функцию — RegisterPowerSettingNotification. Её минус — она поддерживается только начиная с Windows Vista, но меня это не останавливало, т.к. я писал для себя и лично я уже везде пользуюсь Windows 7.
Вот её синтакс:
Эта функция поддерживает как работу в сервисах так и в обычных приложениях(чем мы и воспользуемся).
Для начала я скопировал пример работы с DeskBand в отдельную папку и начал его править.
Вызываю я её в обработчике события WM_CREATE вот так:
h1 и h2 — глобальные(ногами не бить!) переменные типа HPOWERNOTIFY, что есть те же HANDLE по сути своей.
Так мы подписались на получение событий вставки/вынимания кабеля зарядки и изменения оставшегося процента батареи. Так же можно подписаться и на изменение схемы питания используя GUID_POWERSCHEME_PERSONALITY.
Кстати, не забудьте в деструкторе например сделать UnregisterPowerSettingNotification для h1 и h2.
Так же я завёл глобальные(только не по почкам!) переменные для хранения заряда батареи и статуса зарядки:
И в обработчике приходящего события WM_POWERBROADCAST:
Что сие делает? При получении события WM_POWERBROADCAST в lParam передается указатель на структуру POWERBROADCAST_SETTING, которую мы и получаем. Т.к. сама эта структура тоже в меру извращенная, то мы смотрим какому событию соответствует именно эта заполненная структура.
Далее если это событие подключения зарядки — то мы смотрим что получилось. 0 — это батарея, 1 — зарядка, 2 — UPS(ни разу не видел чтобы сабж где-то засветился). Ну и сохраняем результат в переменную isOnBattery(true — если от батареи, false — иначе).
Если же это событие изменения % зарядки батареи — то сохраняем уровень зарядки себе. Далее вызываем перерисовку.
Для начала надо определиться что будем рисовать. Мне было лень искать красивые картинки батареек, поэтому я решил просто вывести красиво текст. Так что сначала я формирую строку:
Далее не забываю сказать, что я вообще-то перерисовываюсь…
Далее два случая — включена композиция рабочего стола или нет. Т.к. я старой темой Windows не пользуюсь и предпочитаю Aero — то в случае отключенной композиции просто вывожу текст:
В случае включенной композиции немного сложнее:
Собственно, тут я считаю, каким цветом рисовать зарядку(66-100% = зеленый, 33-66% = желтый, 0-33% = красный), и вывожу, соответственно, текст.
Вот и собственно всё. Можно собирать проект.
Хочу отметить, что тут я не указывал тот код, что был в примере, только тот, что изменялся или дописывался мной.
После успешной сборки проекта я получил dll-ку.
По опыту работы с COM-компонентами и подобной гадостью — я выполнил от админа следующую комманду в папке с полученной DLL-кой:
Далее я просто включил соответствующую панель через контекстное меню.
Вот и всё.
Вопросы?
Т.к. у меня ноутбуки другого производителя, а искать как выцарапать эту софтину у производителя мне было лень — я начал искать аналог, и, к преглубокому удивлению, ничего не нашел! (если я не прав — покажите носом, буду весьма благодарен!)
Именно так я решил написать своё решение. Писать будем на C++. Я писал в Visual Studio 2010, можно использовать предыдущие версии. Главное — наличие установленного Windows SDK(ставится отдельно от студии, доступен бесплатно, скачать можно например тут)
Вот что у меня получилось:
Больше всего времени у меня занял поиск в интернете названия собственно того куда я хотел запихать отображение данных. Оно называется DeskBand, и что самое хорошее — в Windows SDK есть пример работы его! (у меня он по адресу C:\Program Files\Microsoft SDKs\Windows\v7.0\Samples\winui\shell\shellextensibility\deskbands).
Далее дело стояло за малым — надо найти как получать информацию о заряде батареи. Поисковик в сочетании с MSDN навёл меня на полезную функцию — RegisterPowerSettingNotification. Её минус — она поддерживается только начиная с Windows Vista, но меня это не останавливало, т.к. я писал для себя и лично я уже везде пользуюсь Windows 7.
Вот её синтакс:
Copy Source | Copy HTML
- HPOWERNOTIFY WINAPI RegisterPowerSettingNotification(
- __in HANDLE hRecipient,
- __in LPCGUID PowerSettingGuid,
- __in DWORD Flags
- );
-
Эта функция поддерживает как работу в сервисах так и в обычных приложениях(чем мы и воспользуемся).
Для начала я скопировал пример работы с DeskBand в отдельную папку и начал его править.
Вызываю я её в обработчике события WM_CREATE вот так:
Copy Source | Copy HTML
- h1 = RegisterPowerSettingNotification(pDeskBand->m_hwnd, &GUID_ACDC_POWER_SOURCE, DEVICE_NOTIFY_WINDOW_HANDLE);
- if (h1== 0) pDeskBand->CloseDW(1);
-
- h2 = RegisterPowerSettingNotification(pDeskBand->m_hwnd, &GUID_BATTERY_PERCENTAGE_REMAINING, DEVICE_NOTIFY_WINDOW_HANDLE);
- if (h2== 0)
- {
- UnregisterPowerSettingNotification(h1);
- pDeskBand->CloseDW(1);
- }
-
-
h1 и h2 — глобальные(ногами не бить!) переменные типа HPOWERNOTIFY, что есть те же HANDLE по сути своей.
Так мы подписались на получение событий вставки/вынимания кабеля зарядки и изменения оставшегося процента батареи. Так же можно подписаться и на изменение схемы питания используя GUID_POWERSCHEME_PERSONALITY.
Кстати, не забудьте в деструкторе например сделать UnregisterPowerSettingNotification для h1 и h2.
Так же я завёл глобальные(только не по почкам!) переменные для хранения заряда батареи и статуса зарядки:
Copy Source | Copy HTML
- bool isOnBattery=false;
- int charged=-1;
-
И в обработчике приходящего события WM_POWERBROADCAST:
Copy Source | Copy HTML
- POWERBROADCAST_SETTING* pps = (POWERBROADCAST_SETTING*)lParam;
- if ( sizeof(int) == pps->DataLength && pps->PowerSetting == GUID_ACDC_POWER_SOURCE )
- {
- int nPowerSrc = *(int*)(DWORD_PTR) pps->Data;
- isOnBattery= ( 0 != nPowerSrc);
- }
- else if ( sizeof(int) == pps->DataLength && pps->PowerSetting == GUID_BATTERY_PERCENTAGE_REMAINING )
- {
- charged = *(int*)(DWORD_PTR) pps->Data;
- }
- pDeskBand->OnPaint(NULL);
-
Что сие делает? При получении события WM_POWERBROADCAST в lParam передается указатель на структуру POWERBROADCAST_SETTING, которую мы и получаем. Т.к. сама эта структура тоже в меру извращенная, то мы смотрим какому событию соответствует именно эта заполненная структура.
Далее если это событие подключения зарядки — то мы смотрим что получилось. 0 — это батарея, 1 — зарядка, 2 — UPS(ни разу не видел чтобы сабж где-то засветился). Ну и сохраняем результат в переменную isOnBattery(true — если от батареи, false — иначе).
Если же это событие изменения % зарядки батареи — то сохраняем уровень зарядки себе. Далее вызываем перерисовку.
Для начала надо определиться что будем рисовать. Мне было лень искать красивые картинки батареек, поэтому я решил просто вывести красиво текст. Так что сначала я формирую строку:
Copy Source | Copy HTML
- wchar_t* arr = (wchar_t*)malloc(1024); //не забудьте потом сделать free(arr);
- if (charged>=0)
- _itow(charged, arr,10);
- else
- wcscpy(arr,L"?");
- wcscat(arr,L"%");
- if (!isOnBattery) wcscat(arr,L" (+)");
-
Далее не забываю сказать, что я вообще-то перерисовываюсь…
Copy Source | Copy HTML
- InvalidateRect(m_hwnd,&rc,true);
-
Далее два случая — включена композиция рабочего стола или нет. Т.к. я старой темой Windows не пользуюсь и предпочитаю Aero — то в случае отключенной композиции просто вывожу текст:
Copy Source | Copy HTML
- SetBkColor(hdc, RGB(255, 255, 0));
- GetTextExtentPointW(hdc, arr, wcslen(arr), &size);
- TextOutW(hdc, (RECTWIDTH(rc) - size.cx) / 2, (RECTHEIGHT(rc) - size.cy) / 2, arr, wcslen(arr));
В случае включенной композиции немного сложнее:
Copy Source | Copy HTML
- DTTOPTS dttOpts = {sizeof(dttOpts)};
- dttOpts.dwFlags = DTT_COMPOSITED | DTT_TEXTCOLOR | DTT_GLOWSIZE;
- int red= 0;
- int green= 0;
- if (charged> 0)
- {
- if (charged>66) green = 255;
- else if (charged>33) green = red = 255;
- else red = 255;
- }
- dttOpts.crText = RGB(red, green, 0);
- dttOpts.iGlowSize = 10;
-
- DrawThemeTextEx(hTheme, hdcPaint, 0, 0, arr, -1, 0, &rcText, &dttOpts);
Собственно, тут я считаю, каким цветом рисовать зарядку(66-100% = зеленый, 33-66% = желтый, 0-33% = красный), и вывожу, соответственно, текст.
Вот и собственно всё. Можно собирать проект.
Хочу отметить, что тут я не указывал тот код, что был в примере, только тот, что изменялся или дописывался мной.
После успешной сборки проекта я получил dll-ку.
По опыту работы с COM-компонентами и подобной гадостью — я выполнил от админа следующую комманду в папке с полученной DLL-кой:
regsvr32 DeskbandSDKSample.dll
Далее я просто включил соответствующую панель через контекстное меню.
Вот и всё.
Вопросы?