It all started with the fact that I saw a utility from IBM / Lenovo to show the laptop battery charge in an unusual place - in the taskbar / superbar, but not as an icon, but as a panel (similar ones are used to control iTunes, WMP, Zune players):

Because I have laptops from another manufacturer, and I was too lazy to look for how to scratch this software from the manufacturer - I started looking for an analogue, and, to my great surprise, I did not find anything! (if I'm wrong - show it with your nose, I will be very grateful!)
')
That is how I decided to write my decision. We will write in C ++. I wrote in Visual Studio 2010, you can use previous versions. The main thing is the presence of the installed Windows SDK (installed separately from the studio, available for free, for example, you can download it
here )
Here's what I got:

Most of the time it took me to search the Internet for the name of the actual place where I wanted to cram the display of data. It's called
DeskBand , and best of all - the Windows SDK has an example of how it works! (I have it at C: \ Program Files \ Microsoft SDKs \ Windows \ v7.0 \ Samples \ winui \ shell \ shellextensibility \ deskbands).
Further, the case stood for small - it is necessary to find how to obtain information about the battery charge. Search engine in combination with MSDN brought me to a useful function - RegisterPowerSettingNotification. Its disadvantage is that it is supported only from Windows Vista, but this did not stop me, because I wrote for myself and personally I already use Windows 7 everywhere.
Here is its syntax:
Copy Source | Copy HTML HPOWERNOTIFY WINAPI RegisterPowerSettingNotification( __in HANDLE hRecipient, __in LPCGUID PowerSettingGuid, __in DWORD Flags );
Copy Source | Copy HTML HPOWERNOTIFY WINAPI RegisterPowerSettingNotification( __in HANDLE hRecipient, __in LPCGUID PowerSettingGuid, __in DWORD Flags );
Copy Source | Copy HTML HPOWERNOTIFY WINAPI RegisterPowerSettingNotification( __in HANDLE hRecipient, __in LPCGUID PowerSettingGuid, __in DWORD Flags );
Copy Source | Copy HTML HPOWERNOTIFY WINAPI RegisterPowerSettingNotification( __in HANDLE hRecipient, __in LPCGUID PowerSettingGuid, __in DWORD Flags );
Copy Source | Copy HTML HPOWERNOTIFY WINAPI RegisterPowerSettingNotification( __in HANDLE hRecipient, __in LPCGUID PowerSettingGuid, __in DWORD Flags );
Copy Source | Copy HTML HPOWERNOTIFY WINAPI RegisterPowerSettingNotification( __in HANDLE hRecipient, __in LPCGUID PowerSettingGuid, __in DWORD Flags );
This feature supports both work in services and in normal applications (which we will use).
To start, I copied the example of working with DeskBand into a separate folder and began to edit it.
I call it in the WM_CREATE event handler like this:
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 and h2 are global (do not beat with your feet!) variables of type HPOWERNOTIFY, which is essentially the same HANDLE.
So we signed up to receive cable insertion / removal events and changes in the remaining battery percentage. You can also subscribe to change the power scheme using GUID_POWERSCHEME_PERSONALITY.
By the way, do not forget to do UnregisterPowerSettingNotification for h1 and h2 in a destructor for example.
I also started global (not kidneys only!) Variables for storing battery charge and charging status:
Copy Source | Copy HTML
- bool isOnBattery = false ;
- int charged = -1;
And in the handler of the incoming WM_POWERBROADCAST event:
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);
What does this do? When the WM_POWERBROADCAST event is received, a pointer to the POWERBROADCAST_SETTING structure is passed to lParam, which we get. Because this structure itself is also moderately perverted, then we are looking at which event this particular structure corresponds to.
Further, if this is a charging connection event, then we see what happened. 0 is a battery, 1 is charging, 2 is UPS (I have never seen a subject lit up somewhere). Well, save the result in the variable isOnBattery (true - if the battery, false - otherwise).
If this event changes the% of the battery charge, then we keep the charge level for ourselves. Next, call the redraw.
First you need to decide what we will draw. I was too lazy to look for beautiful pictures of batteries, so I decided to just print the text beautifully. So first I form the line:
Copy Source | Copy HTML
- wchar_t * arr = (wchar_t *) malloc (1024); // don't forget to do it later free (arr);
- if (charged> = 0)
- _itow (charged, arr, 10);
- else
- wcscpy (arr, L "?");
- wcscat (arr, L "%");
- if (! isOnBattery) wcscat (arr, L "(+)");
Then I remember saying that I actually redraw ...
Copy Source | Copy HTML
- InvalidateRect (m_hwnd, & rc, true );
Further, two cases - included desktop composition or not. Because I do not use the old Windows theme and prefer Aero - then in the case of a disabled song I simply output the text:
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));
In the case of the included composition is a bit more complicated:
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);
Actually, here I consider what color to draw charging (66-100% = green, 33-66% = yellow, 0-33% = red), and I draw, accordingly, the text.
That's all. You can build a project.
I want to note that here I did not indicate the code that was in the example, only the one that was changed or added by me.
After a successful build project, I got a dll-ku.
From the experience of working with COM components and similar filth, I have executed the following command from the admin in the folder with the received DLL:
regsvr32 DeskbandSDKSample.dll
Then I just turned on the corresponding panel through the context menu.
That's all.
Questions?