KOO
KOO

Reputation: 61

Battery FullChargeCapacity using WMI returns 0

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

Answers (1)

huysentruitw
huysentruitw

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

Related Questions