使用WMI的电池电量已满返回0

Battery FullChargeCapacity using WMI returns 0

本文关键字:返回 满返回 WMI 使用      更新时间:2023-10-16

我尝试在C++中使用WMI。在WMI类CIM_Battery中,FullChargeCapacity的值仅返回0。

有其他方法可以得到FullChargeCapacity吗?

我尝试的代码是:

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();
}

正如您自己发现的那样,使用WMI将无法获得您想要的信息,您必须直接与电池驱动程序交谈。

电池驱动器通过I²C与电池通信,并可以访问这些值。幸运的是,您可以使用一些特定于电池的IOCLT代码。

您首先需要知道电池驱动程序的设备名称。下面的方法使用SetupApi,但您也可以从HKEY_LOCAL_MACKINESYSTEMCurrentControlSetServicesCmBattEnum的注册表中获取它(假设您的电池使用CmBatt.sys驱动程序)。我确实建议使用SetupApi。

以下是获取设备名称:的辅助函数

#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;
}

之后,您可以使用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);

现在我们需要电池标签,这是驱动程序生成的电池的标识符。如果你更换电池,驱动程序会生成一个新标签。有了这个标签,我们可以查询其余的电池信息(请参阅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代码和相关结构概述在MSDN电源管理控制代码上

更新

根据您的评论中的要求,为了读取时间估计,请像前一个片段中那样在bqi中请求标签,然后像这样查询BatteryEstimatedTime

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;
}

用于读取实际电压和容量:

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;
}

通过比较bs.Capacitybi.FullChargedCapacity,您可以获得电池剩余容量的百分比指示。