Reputation: 61
I tried using WMI in C++. In the WMI class CIM_Battery
, FullChargeCapacity
value just returns 0.
Is there any other method to get FullChargeCapacity
?
The code I tried is:
IWbemClassObject *pclsObj = NULL;
ULONG uReturn = 0;
while (pEnumerator)
{
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1,
&pclsObj, &uReturn);
if (0 == uReturn)
{
break;
}
VARIANT vtProp;
// Get the value of the Name property
hr = pclsObj->Get(L"FullChargeCapacity", 0, &vtProp, 0, 0);
wcout << " FullChargeCapacity : " << vtProp.ulVal << endl;
VariantClear(&vtProp);
/*hr = pclsObj->Get(L"Name", 0, &vtProp, 0, 0);
wcout << " Name : " << vtProp.bstrVal << endl;
VariantClear(&vtProp);*/
pclsObj->Release();
}
Upvotes: 1
Views: 890
Reputation: 28151
As you figured out yourself, with WMI you will not get the information you want, you'll have to talk with the battery driver directly.
The battery driver communicates with the battery over I²C and has access to these values. Luckily, there are some battery specific IOCLT codes you can use.
You first need to know the deviceName of the battery's driver. Below method uses SetupApi for that, but you could also get it from the registry at HKEY_LOCAL_MACKINE\SYSTEM\CurrentControlSet\Services\CmBatt\Enum\0
(assuming your battery uses the CmBatt.sys driver). I do recommend using SetupApi.
Below is a helper function to get the deviceName:
#include <Windows.h>
#include <SetupAPI.h>
#include <batclass.h>
#include <devguid.h>
#include <string>
#include <iostream>
#ifndef UNICODE
typedef std::string String;
#else
typedef std::wstring String;
#endif
const String GetBatteryDevicePath(const int batteryIndex)
{
String result = TEXT("");
HDEVINFO hDeviceInfoList = ::SetupDiGetClassDevs(&GUID_DEVCLASS_BATTERY, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hDeviceInfoList != INVALID_HANDLE_VALUE)
{
SP_DEVICE_INTERFACE_DATA did = { 0 };
did.cbSize = sizeof(did);
if (::SetupDiEnumDeviceInterfaces(hDeviceInfoList, 0, &GUID_DEVCLASS_BATTERY, batteryIndex, &did))
{
DWORD dwRequiredSize = 0;
::SetupDiGetDeviceInterfaceDetail(hDeviceInfoList, &did, 0, 0, &dwRequiredSize, 0);
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
PSP_DEVICE_INTERFACE_DETAIL_DATA pdidd = (PSP_DEVICE_INTERFACE_DETAIL_DATA)::LocalAlloc(LPTR, dwRequiredSize);
if (pdidd)
{
pdidd->cbSize = sizeof(*pdidd);
if (::SetupDiGetDeviceInterfaceDetail(hDeviceInfoList, &did, pdidd, dwRequiredSize, &dwRequiredSize, 0))
{
result = pdidd->DevicePath;
}
::LocalFree(pdidd);
}
}
}
::SetupDiDestroyDeviceInfoList(hDeviceInfoList);
}
return result;
}
After that, you can open a handle to the battery device using CreateFile
:
const String devicePath = ::GetBatteryDevicePath(0);
const auto hBattery = ::CreateFile(devicePath.c_str(),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
Now we need the battery tag, this is an identifier for the battery generated by the driver. F.e. if you swap batteries, the driver will generate a new tag. With this tag we can query the rest of the battery information (see BATTERY_INFORMATION):
if (hBattery != INVALID_HANDLE_VALUE)
{
DWORD dwBytesReturned, dwWait = 0;
BATTERY_QUERY_INFORMATION bqi = { 0 };
// Query battery tag
if (::DeviceIoControl(hBattery,
IOCTL_BATTERY_QUERY_TAG,
&dwWait,
sizeof(dwWait),
&bqi.BatteryTag,
sizeof(bqi.BatteryTag),
&dwBytesReturned,
NULL)
&& bqi.BatteryTag)
{
// Now we can query all other battery info
BATTERY_INFORMATION bi = { 0 };
if (::DeviceIoControl(hBattery,
IOCTL_BATTERY_QUERY_INFORMATION,
&bqi,
sizeof(bqi),
&bi,
sizeof(bi),
&dwBytesReturned,
NULL))
{
std::cout << "FullChargedCapacity = " << bi.FullChargedCapacity << std::endl;
std::cout << "DesignedCapacity = " << bi.DesignedCapacity << std::endl;
}
}
::CloseHandle(hBattery);
}
IOCTL codes and related structs are outlined on MSDN Power Management Control Codes
UPDATE
As requested in your comment, for reading out the time estimation, request the tag in bqi
as in the previous snippet, then query BatteryEstimatedTime
like this:
bqi.AtRate = 0;
bqi.InformationLevel = BatteryEstimatedTime;
ULONG lEstimatedTime;
if (::DeviceIoControl(hBattery,
IOCTL_BATTERY_QUERY_INFORMATION,
&bqi,
sizeof(bqi),
&lEstimatedTime,
sizeof(lEstimatedTime),
&dwBytesReturned,
NULL))
{
if (lEstimatedTime != BATTERY_UNKNOWN_TIME)
std::cout << "EstimatedTime = " << lEstimatedTime / 60 << "m" << std::endl;
else
std::cout << "EstimatedTime = UNKNOWN" << std::endl;
}
For reading out the actual voltage and capacity:
BATTERY_WAIT_STATUS bws = { 0 };
BATTERY_STATUS bs = { 0 };
bws.BatteryTag = bqi.BatteryTag;
if (::DeviceIoControl(hBattery,
IOCTL_BATTERY_QUERY_STATUS,
&bws,
sizeof(bws),
&bs,
sizeof(bs),
&dwBytesReturned,
NULL))
{
if (bs.PowerState & BATTERY_CHARGING)
std::cout << "Battery is CHARGING" << std::endl;
if (bs.PowerState & BATTERY_DISCHARGING)
std::cout << "Battery is DISCHARGING" << std::endl;
if (bs.PowerState & BATTERY_POWER_ON_LINE)
std::cout << "Power on-line" << std::endl;
std::cout << "Battery voltage: " << bs.Voltage << "mV" << std::endl;
std::cout << "Battery capacity: " << bs.Capacity << "mW" << std::endl;
}
By comparing bs.Capacity
against the bi.FullChargedCapacity
you can get a percentage indication of the remaining capacity of the battery.
Upvotes: 1