Pull to refresh

Как сплагиатить удобную утилиту для показа зарядки ноутбука?

Programming
Всё началось с того что я увидел утилиту от 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.

Вот её синтакс:

Copy Source | Copy HTML
  1. HPOWERNOTIFY WINAPI RegisterPowerSettingNotification(
  2. __in HANDLE hRecipient,
  3. __in LPCGUID PowerSettingGuid,
  4. __in DWORD Flags
  5. );


Эта функция поддерживает как работу в сервисах так и в обычных приложениях(чем мы и воспользуемся).

Для начала я скопировал пример работы с DeskBand в отдельную папку и начал его править.

Вызываю я её в обработчике события WM_CREATE вот так:

Copy Source | Copy HTML
  1. h1 = RegisterPowerSettingNotification(pDeskBand->m_hwnd, &GUID_ACDC_POWER_SOURCE, DEVICE_NOTIFY_WINDOW_HANDLE);
  2. if (h1== 0) pDeskBand->CloseDW(1);
  3. h2 = RegisterPowerSettingNotification(pDeskBand->m_hwnd, &GUID_BATTERY_PERCENTAGE_REMAINING, DEVICE_NOTIFY_WINDOW_HANDLE);
  4. if (h2== 0)
  5. {
  6. UnregisterPowerSettingNotification(h1);
  7. pDeskBand->CloseDW(1);
  8. }


h1 и h2 — глобальные(ногами не бить!) переменные типа HPOWERNOTIFY, что есть те же HANDLE по сути своей.
Так мы подписались на получение событий вставки/вынимания кабеля зарядки и изменения оставшегося процента батареи. Так же можно подписаться и на изменение схемы питания используя GUID_POWERSCHEME_PERSONALITY.
Кстати, не забудьте в деструкторе например сделать UnregisterPowerSettingNotification для h1 и h2.

Так же я завёл глобальные(только не по почкам!) переменные для хранения заряда батареи и статуса зарядки:
Copy Source | Copy HTML
  1. bool isOnBattery=false;
  2. int charged=-1;


И в обработчике приходящего события WM_POWERBROADCAST:

Copy Source | Copy HTML
  1. POWERBROADCAST_SETTING* pps = (POWERBROADCAST_SETTING*)lParam;
  2. if ( sizeof(int) == pps->DataLength && pps->PowerSetting == GUID_ACDC_POWER_SOURCE )
  3. {
  4. int nPowerSrc = *(int*)(DWORD_PTR) pps->Data;
  5. isOnBattery= ( 0 != nPowerSrc);
  6. }
  7. else if ( sizeof(int) == pps->DataLength && pps->PowerSetting == GUID_BATTERY_PERCENTAGE_REMAINING )
  8. {
  9. charged = *(int*)(DWORD_PTR) pps->Data;
  10. }
  11. pDeskBand->OnPaint(NULL);


Что сие делает? При получении события WM_POWERBROADCAST в lParam передается указатель на структуру POWERBROADCAST_SETTING, которую мы и получаем. Т.к. сама эта структура тоже в меру извращенная, то мы смотрим какому событию соответствует именно эта заполненная структура.

Далее если это событие подключения зарядки — то мы смотрим что получилось. 0 — это батарея, 1 — зарядка, 2 — UPS(ни разу не видел чтобы сабж где-то засветился). Ну и сохраняем результат в переменную isOnBattery(true — если от батареи, false — иначе).

Если же это событие изменения % зарядки батареи — то сохраняем уровень зарядки себе. Далее вызываем перерисовку.

Для начала надо определиться что будем рисовать. Мне было лень искать красивые картинки батареек, поэтому я решил просто вывести красиво текст. Так что сначала я формирую строку:

Copy Source | Copy HTML
  1. wchar_t* arr = (wchar_t*)malloc(1024); //не забудьте потом сделать free(arr);
  2. if (charged>=0)
  3. _itow(charged, arr,10);
  4. else
  5. wcscpy(arr,L"?");
  6. wcscat(arr,L"%");
  7. if (!isOnBattery) wcscat(arr,L" (+)");


Далее не забываю сказать, что я вообще-то перерисовываюсь…

Copy Source | Copy HTML
  1. InvalidateRect(m_hwnd,&rc,true);


Далее два случая — включена композиция рабочего стола или нет. Т.к. я старой темой Windows не пользуюсь и предпочитаю Aero — то в случае отключенной композиции просто вывожу текст:

Copy Source | Copy HTML
  1. SetBkColor(hdc, RGB(255, 255, 0));
  2. GetTextExtentPointW(hdc, arr, wcslen(arr), &size);
  3. TextOutW(hdc, (RECTWIDTH(rc) - size.cx) / 2, (RECTHEIGHT(rc) - size.cy) / 2, arr, wcslen(arr));


В случае включенной композиции немного сложнее:

Copy Source | Copy HTML
  1. DTTOPTS dttOpts = {sizeof(dttOpts)};
  2. dttOpts.dwFlags = DTT_COMPOSITED | DTT_TEXTCOLOR | DTT_GLOWSIZE;
  3. int red= 0;
  4. int green= 0;
  5. if (charged> 0)
  6. {
  7. if (charged>66) green = 255;
  8. else if (charged>33) green = red = 255;
  9. else red = 255;
  10. }
  11. dttOpts.crText = RGB(red, green, 0);
  12. dttOpts.iGlowSize = 10;
  13. DrawThemeTextEx(hTheme, hdcPaint, 0, 0, arr, -1, 0, &rcText, &dttOpts);


Собственно, тут я считаю, каким цветом рисовать зарядку(66-100% = зеленый, 33-66% = желтый, 0-33% = красный), и вывожу, соответственно, текст.

Вот и собственно всё. Можно собирать проект.
Хочу отметить, что тут я не указывал тот код, что был в примере, только тот, что изменялся или дописывался мной.
После успешной сборки проекта я получил dll-ку.

По опыту работы с COM-компонентами и подобной гадостью — я выполнил от админа следующую комманду в папке с полученной DLL-кой:
regsvr32 DeskbandSDKSample.dll


Далее я просто включил соответствующую панель через контекстное меню.

Вот и всё.

Вопросы?
Tags:WindowsDeskBandC++панелькапитаниебатарея
Hubs: Programming
Rating +32
Views 796 Add to bookmarks 10
Comments
Comments 40

Popular right now

Top of the last 24 hours