草庐IT

Windows Kernel32.BatteryLifePercent = 255

coder 2024-06-09 原文

我正在尝试构建一个 Java 应用程序来读取笔记本电脑电池的状态,并在电量不足时向用户发送通知。为了做到这一点,我将 jna 与 Kernel32 native 库一起使用,如本问题的第一个答案中所述: How to get the remaining battery life in a Windows system?

运行示例,程序产生以下输出:

ACLineStatus: Offline
Battery Flag: High, more than 66 percent
Battery Life: Unknown
Battery Left: 0 seconds
Battery Full: 10832 seconds

电池生命周期和电池剩余字段在 Kernel32 BatteryLifePercentBatteryLifeTime 值中读取,它们分别是 255(未知)和 0(我没有得到这个值。未知)根据此处的 Microsoft 文档,将为 -1: https://msdn.microsoft.com/en-us/library/windows/desktop/aa373232(v=vs.85).aspx ).

我的问题是:为什么我要恢复这些值? Windows 电池托盘图标显示了正确的百分比,那么为什么我无法从此处获取该数据?

我运行的是 64 位 Windows 7 旗舰版。

谢谢。

最佳答案

链接答案中的代码是错误的(编辑:现在 它是固定的)。

字段以错误的方式排序,将 getFieldOrder 方法更改为

@Override
protected List<String> getFieldOrder() 
{
    ArrayList<String> fields = new ArrayList<String>();
    fields.add("ACLineStatus");
    fields.add("BatteryFlag");
    fields.add("BatteryLifePercent");
    fields.add("Reserved1");
    fields.add("BatteryLifeTime");
    fields.add("BatteryFullLifeTime");
    return fields;
}

同时添加指定正确对齐方式的构造函数

 public SYSTEM_POWER_STATUS()
 {
    setAlignType(ALIGN_MSVC);
 }

对齐方式也可以是 ALIGN_NONE,因为 Microsoft 通常会注意将数据与保留字段显式对齐。
它也可以是 ALIGN_DEFAULT,因为据我所知,Windows 是使用 Microsoft 编译器编译的,它会在数据的自然边界上对齐数据。

换句话说,该结构是通过设计自然对齐的,因此不需要特定的对齐约束


这是我系统上原始代码的输出

ACLineStatus: Offline
Battery Flag: High, more than 66 percent
Battery Life: Unknown
Battery Left: 0 seconds
Battery Full: 12434 seconds

这是修改后的代码

ACLineStatus: Offline
Battery Flag: High, more than 66 percent
Battery Life: 95%
Battery Left: 12434 seconds
Battery Full: Unknown


为什么会这样

考虑到上面的输出,我们可以重构结构SYSTEM_POWER_STATUS是如何填充到内存中的。

    00 08 5f 00 96 30 00 00 ff ff ff ff
    ¯¯ ¯¯ ¯¯ ¯¯ ¯¯¯¯¯¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯¯¯¯
     |  | |  |          |             |   
     |  | |  |       BatteryLifeTime  |
     |  | | Reserved1                 |
     |  | |                      BatteryFullLifeTime     
     |  | BatteryLifePercent
     |  |
     | BatteryFlags
     |
 AcLineStatus

按照原代码的字段顺序,字段是这样初始化的

    00 08 5f 00 96 30 00 00 ff ff ff ff  00 00 00 00
    ¯¯ ¯¯       ¯¯¯¯¯¯¯¯¯¯¯ ¯¯           ¯¯¯¯¯¯¯¯¯¯¯
    |  |             |      |                    |
    | BatteryFlags   |     BatteryLifePercent    |
    |                |                           |
AcLineStatus         |                         BatteryLifeTime
                 BatteryFullLifeTime

这些间隙是由于默认对齐方式造成的,默认对齐方式使数据在其自然边界上对齐。
由于字段已重新排序,它们不再位于原来的位置并且是连续的。

关于为什么 BatteryFullLifeTime 未知

如果你为 Win7 64 位反汇编函数 GetSystemPowerStatus(你可以找到我的反汇编 here)并重写一个等效的 C 程序,你会得到类似这样的东西

BOOL WINAPI GetSystemPowerStatus(
  _Out_ LPSYSTEM_POWER_STATUS lpSystemPowerStatus
)
{
    SYSTEM_BATTERY_STATE battery_state;

    //Get power information
    NTStatus pi_status = NtPowerInformation(SystemBatteryState, NULL, 0, &battery_state, sizeof(battery_state));

    //Check success
    if (!NTSuccess(pi_status))
    {
        BaseSetLastNtError(pi_status);
        return FALSE;
    }

    //Zero out the input structure
    memset(lpSystemPowerStatus, sizeof(lpSystemPowerStatus), 0);

    //Set AC line status
    lpSystemPowerStatus->ACLineStatus = battery_state.BatteryPresent && battery_state.AcOnLine ? 1 : 0;

    //Set flags
    lpSystemPowerStatus->BatteryFlags   |=  (battery_state.Charging         ? 8 :    0) 
                                        |   (battery_state.BatteryPresent   ? 0 : 0x80);



    //Set battery life time percent
    lpSystemPowerStatus->BatteryLifePercent = 0xff;
    if (battery_state.MaxCapacity)
    {
        lpSystemPowerStatus->BatteryLifePercent = battery_state.RemainingCapacity > battery_state.MaxCapacity
                                                ? 100
                                                : (battery_state.RemainingCapacity*100 + battery_state.MaxCapacity/2)/battery_state.MaxCapacity;

        lpSystemPowerStatus->BatteryFlags   |=  (lpSystemPowerStatus->BatteryLifePercent > 66 ? 1 : 0) 
                                            |   (lpSystemPowerStatus->BatteryLifePercent < 33 ? 2 : 0);
    }

    //Set battery life time and full life time
    lpSystemPowerStatus->BatteryLifeTime = lpSystemPowerStatus->BatteryFullLifeTime = -1;

    if (battery_state.EstimatedTime)
        lpSystemPowerStatus->BatteryLifeTime = battery_state.EstimatedTime;
}

这表明 BatterFullLifeTime 永远不会从 SYSTEM_BATTERY_STATE 结构中复制。它总是-1。
此外,永远不会设置值为 4(临界电池电量)的标志。
在较新版本的 Windows 中,这些问题可能已得到修复。


较新的版本

您可以调用PowrProf.dll 中的CallNtPowerInformation 来获取更可靠的电池状态信息。

如果您不熟悉访问 Win API,这里有一个 JNA 类可以为您完成这项工作

PowrProf.Java

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package javaapplication5;

/**
 *
 * @author mijo
 */
import java.util.List;

import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.win32.StdCallLibrary;
import java.util.Arrays;

public interface PowrProf extends StdCallLibrary {

    public PowrProf INSTANCE = (PowrProf) Native.loadLibrary("PowrProf", PowrProf.class);


    public class SYSTEM_BATTERY_STATE extends Structure 
    {
        public static class ByReference extends SYSTEM_BATTERY_STATE implements Structure.ByReference {}

        public byte AcOnLine;
        public byte BatteryPresent;
        public byte Charging;
        public byte Discharging;

        public byte Spare1_0;
        public byte Spare1_1;
        public byte Spare1_2;
        public byte Spare1_3;

        public int   MaxCapacity;
        public int   RemainingCapacity;
        public int   Rate;
        public int   EstimatedTime;
        public int   DefaultAlert1;
        public int   DefaultAlert2;

        @Override
        protected List<String> getFieldOrder() 
        {
            return Arrays.asList(new String[]
            {
                "AcOnLine", "BatteryPresent", "Charging", "Discharging", 
                "Spare1_0", "Spare1_1", "Spare1_2", "Spare1_3", 
                "MaxCapacity", "RemainingCapacity", "Rate", 
                "EstimatedTime", "DefaultAlert1", "DefaultAlert2"
            });
        }

        public SYSTEM_BATTERY_STATE ()
        {
            setAlignType(ALIGN_MSVC);
        }

        public boolean isAcConnected()
        {
            return AcOnLine != 0;
        }

        public boolean isBatteryPresent()
        {
            return BatteryPresent != 0;
        }


        public enum BatteryFlow{ Charging, Discharging, None }

        public BatteryFlow getBatteryFlow()
        {
            if (Charging != 0)       return BatteryFlow.Charging;
            if (Discharging != 0)    return BatteryFlow.Discharging;

            return BatteryFlow.None;
        }

        //in mWh
        public int getMaxCapacity()
        {
            return MaxCapacity;
        }

        //in mWh
        public int getCurrentCharge()
        {
            return RemainingCapacity;
        }

        //in mW
        public int getFlowRate()
        {
            return Rate;
        }

        //in s
        public int getEstimatedTime()
        {
            return EstimatedTime;
        }

        //in s
        //-1 if not available
        public int getTimeToEmpty()
        {
            if (getBatteryFlow() != BatteryFlow.Discharging)
                return -1;

            return -getCurrentCharge()*3600/getFlowRate();
        }

        //in s
        //-1 if not available
        public int getTimeToFull()
        {
            if (getBatteryFlow() != BatteryFlow.Charging)
                return -1;

            return (getMaxCapacity()-getCurrentCharge())*3600/getFlowRate();
        }

        public double getCurrentChargePercent()
        {
            return getCurrentCharge()*100/getMaxCapacity();
        }

        public int getCurrentChargeIntegralPercent()
        {
            return (getCurrentCharge()*100+getMaxCapacity()/2)/getMaxCapacity();
        }

        @Override
        public String toString()
        {
            StringBuilder b = new StringBuilder(4096);

            b.append("AC Line? "); b.append(isAcConnected());
            b.append("\nBattery present? "); b.append(isBatteryPresent());
            b.append("\nBattery flow: "); b.append(getBatteryFlow());
            b.append("\nMax capacity (mWh): "); b.append(getMaxCapacity());
            b.append("\nCurrent charge (mWh): "); b.append(getCurrentCharge());
            b.append("\nFlow rate (mW/s): "); b.append(getFlowRate());
            b.append("\nEstimated time (from OS): "); b.append(getEstimatedTime());
            b.append("\nEstimated time (manual): "); b.append(getTimeToEmpty());
            b.append("\nEstimated time to full (manual): "); b.append(getTimeToFull());
            b.append("\nCurrent charge (percent): "); b.append(getCurrentChargePercent());
            b.append("\nCurrent charge (integral percent): "); b.append(getCurrentChargeIntegralPercent());

            return b.toString();
        }
    }

    public int CallNtPowerInformation(int informationLevel, Pointer  inBuffer, long inBufferLen, SYSTEM_BATTERY_STATE.ByReference  outBuffer, long outBufferLen);

    static final int SystemBatteryState = 5;

    public static SYSTEM_BATTERY_STATE GetBatteryState()
    {
        SYSTEM_BATTERY_STATE.ByReference battery_state = new SYSTEM_BATTERY_STATE.ByReference();

        int retVal = PowrProf.INSTANCE.CallNtPowerInformation(SystemBatteryState, Pointer.NULL, 0, battery_state, battery_state.size());

        if (retVal != 0)
            return null;

        return battery_state;
    }
}

及其用途

public static void main(String[] args) 
{
    PowrProf.SYSTEM_BATTERY_STATE sbs = PowrProf.GetBatteryState();

    System.out.println(sbs);
} 

放电时的示例输出:

AC Line? false
Battery present? true
Battery flow: Discharging
Max capacity (mWh): 35090
Current charge (mWh): 34160
Flow rate (mW/s): -11234
Estimated time (from OS): 10940
Estimated time (manual): 10946
Estimated time to full (manual): -1
Current charge (percent): 97.34
Current charge (integral percent): 98

充电时的示例输出:

AC Line? true
Battery present? true
Battery flow: Charging
Max capacity (mWh): 35090
Current charge (mWh): 33710
Flow rate (mW/s): 3529
Estimated time (from OS): -1
Estimated time (manual): -1
Estimated time to full (manual): 1407 Current charge (percent): 96.06
Current charge (integral percent): 97


注意插拔电源线测试时,由于监控不是实时的,请稍等片刻。

附言
我用笔名 Mijo 签署了我的代码,您可以删除该评论。

关于Windows Kernel32.BatteryLifePercent = 255,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35864321/

有关Windows Kernel32.BatteryLifePercent = 255的更多相关文章

  1. STM32读取串口传感器数据(颗粒物传感器,主动上传) - 2

    文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,

  2. STM32的HAL和LL库区别和性能对比 - 2

    LL库和HAL库简介LL:Low-Layer,底层库HAL:HardwareAbstractionLayer,硬件抽象层库LL库和hal库对比,很精简,这实际上是一个精简的库。LL库的配置选择如下:在STM32CUBEMX中,点击菜单的“ProjectManager”–>“AdvancedSettings”,在下面的界面中选择“AdvancedSettings”,然后在每个模块后面选择使用的库总结:1、如果使用的MCU是小容量的,那么STM32CubeLL将是最佳选择;2、如果结合可移植性和优化,使用STM32CubeHAL并使用特定的优化实现替换一些调用,可保持最大的可移植性。另外HAL和L

  3. ESP32学习入门:WiFi连接网络 - 2

    目录一、ESP32简单介绍二、ESP32Wi-Fi模块介绍三、ESP32Wi-Fi编程模型四、ESP32Wi-Fi事件处理流程 五、ESP32Wi-Fi开发环境六、ESP32Wi-Fi具体代码七、ESP32Wi-Fi代码解读6.1主程序app_main7.2自定义代码wifi_init_sta()八、ESP32Wi-Fi连接验证8.1测试方法8.2服务器模拟工具sscom58.3测试代码8.4测试结果前言为了开发一款亚马逊物联网产品,开始入手ESP32模块。为了能够记录自己的学习过程,特记录如下操作过程。一、ESP32简单介绍ESP32是一套Wi-Fi(2.4GHz)和蓝牙(4.2)双模解决方

  4. Spring Security 6.0系列【32】授权服务器篇之默认过滤器 - 2

    有道无术,术尚可求,有术无道,止于术。本系列SpringBoot版本3.0.4本系列SpringSecurity版本6.0.2本系列SpringAuthorizationServer版本1.0.2源码地址:https://gitee.com/pearl-organization/study-spring-security-demo文章目录前言1.OAuth2AuthorizationServerMetadataEndpointFilter2.OAuth2AuthorizationEndpointFilter3.OidcProviderConfigurationEndpointFilter4.N

  5. ruby - 摘要::CRC32 与 Zlib - 2

    在我的代码中,我需要使用各种算法(包括CRC32)对文件进行哈希处理。因为我还在Digest系列中使用其他加密哈希函数,所以我认为为它们维护一个一致的接口(interface)会很好。为了记录,我确实找到了digest-crc,一颗完全符合我要求的gem。问题是,Zlib是标准库的一部分,并且有一个我想重用的CRC32工作实现。此外,它是用C编写的,因此它应该提供与digest-crc相关的卓越性能,后者是纯ruby​​实现。实现Digest::CRC32一开始看起来非常简单:%w(digestzlib).each{|f|requiref}classDigest::CRC32一切正常:

  6. ruby - 安装gem : Couldn't reserve space for cygwin's heap, Win32错误487错误 - 2

    我正在尝试在我的机器上安装win32-apigem,但在构建native扩展时我遇到了一些问题:$geminstallwin32-api--no-ri--rdocTemporarilyenhancingPATHtoincludeDevKit...Buildingnativeextensions.Thiscouldtakeawhile...C:\Programs\dev_kit\bin\make.exe:***Couldn'treservespaceforcygwin'sheap,Win32error0ERROR:Errorinstallingwin32-api:ERROR:Failed

  7. Ruby 1.9 - 没有这样的文件可以加载 'win32/open3' - 2

    我在Windows上运行ruby​​1.9.2并试图移植在Ruby1.8中工作的代码。该代码使用以前运行良好的Open4.popen4。对于1.9.2,我做了以下事情:通过geminstallPOpen4安装了POpen4需要POpen4通过require'popen4'尝试像这样使用POpen4:Open4.popen4("cmd"){|io_in,io_out,io_er|...}当我这样做时,我得到了错误:nosuchfiletoload--win32/open3如果我尝试安装win32-open3(geminstallwin32-open3),我会收到错误消息:win32-op

  8. Dell Inspiron 5488加内存32G - 2

    DellInspiron5488加内存32G 原装内置内存仅仅8G,目前看,真的太小了! 1.内存型号Dell5488内存型号:DDR42666。笔记本有两个内存插槽,原装占了一个,还能扩展一个。 2.买内存如果买Dell原装笔记本内存,8G就得500块左右。 我咨询了一下,三星的笔记本内存,可以兼容。16G,299块(2023年2月23日,京东价) Dell5488内存组合,最多只能插两根16G内存。 我于是买了两根三星16G内存。装上,很爽😄 跑国产系统统信UOS,再也看不到用交换区了,32G内存,爽!  

  9. 蓝桥杯 stm32 MCP4017 - 2

    本文代码使用HAL库。文章目录前言一、MCP4017的重要特性二、MCP4017计算RBW阻值三、MCP4017地址四、MCP4017读写函数五、CubeMX创建工程(利用ADC测量MCP4017电压)、对应代码:总结前言一、MCP4017的重要特性蓝桥杯板子上的是MCP4017T-104ELT,如图1。MCP4017是一个可编程电阻,通过写入的数值可以改变电阻的大小。重点在于6引脚(W),5引脚(B&#

  10. STM32 OTA应用开发——通过USB实现OTA升级 - 2

    STM32OTA应用开发——通过USB实现OTA升级目录STM32OTA应用开发——通过USB实现OTA升级前言1环境搭建2功能描述3BootLoader的制作4APP的制作5烧录下载配置6运行测试结束语前言什么是OTA?百度百科:空中下载技术(Over-the-AirTechnology;OTA),是通过移动通信的空中接口实现对移动终端设备及SIM卡数据进行远程管理的技术。经过公网多年的应用与发展,已十分成熟,网络运营商通过OTA技术实现SIM卡远程管理,还能提供移动化的新业务下载功能。实际上,现在我们所说的OTA比百度百科的定义还要更广泛,OTA的形式已经不再局限于手机和SIM卡,只要涉及

随机推荐