Windows Kernel32.BatteryLifePercent = 255

Aur*_*ere 3 windows battery jna windows-7-x64 kernel32

我正在尝试构建一个 Java 应用程序,该应用程序读取笔记本电脑电池的状态,并在电量不足时向用户发送通知。为了做到这一点,我将 jna 与 Kernel32 本机库一起使用,如该问题的第一个答案中所述: 如何在 Windows 系统中获得剩余电池寿命?

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

ACLineStatus: Offline
Battery Flag: High, more than 66 percent
Battery Life: Unknown
Battery Left: 0 seconds
Battery Full: 10832 seconds
Run Code Online (Sandbox Code Playgroud)

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

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

我正在运行 64 位 Windows 7 Ultimate Edition。

谢谢你。

Mar*_*oom 5

链接答案中的代码是错误的(编辑:现在已修复)。

字段以错误的方式排序,更改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;
}
Run Code Online (Sandbox Code Playgroud)

还要添加此构造函数以指定正确的对齐方式

 public SYSTEM_POWER_STATUS()
 {
    setAlignType(ALIGN_MSVC);
 }
Run Code Online (Sandbox Code Playgroud)

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

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


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

ACLineStatus:离线
电池标志:高,超过 66%
电池寿命:未知
电池剩余时间:0 秒
电池充满:12434 秒

这是带有更正代码的输出

ACLineStatus:离线
电池标志:高,超过 66%
电池寿命:95%
剩余电量:12434 秒
电池已满:未知


为什么会发生这种情况

考虑到上面的输出,我们可以重构结构SYSTEM_POWER_STATUS在内存中的填充方式。

    00 08 5f 00 96 30 00 00 ff ff ff ff
    ¯¯ ¯¯ ¯¯ ¯¯ ¯¯¯¯¯¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯¯¯¯
     |  | |  |          |             |   
     |  | |  |       BatteryLifeTime  |
     |  | | Reserved1                 |
     |  | |                      BatteryFullLifeTime     
     |  | BatteryLifePercent
     |  |
     | BatteryFlags
     |
 AcLineStatus
Run Code Online (Sandbox Code Playgroud)

根据原始代码的字段顺序,这就是字段的初始化方式

    00 08 5f 00 96 30 00 00 ff ff ff ff  00 00 00 00
    ¯¯ ¯¯       ¯¯¯¯¯¯¯¯¯¯¯ ¯¯           ¯¯¯¯¯¯¯¯¯¯¯
    |  |             |      |                    |
    | BatteryFlags   |     BatteryLifePercent    |
    |                |                           |
AcLineStatus         |                         BatteryLifeTime
                 BatteryFullLifeTime
Run Code Online (Sandbox Code Playgroud)

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

关于为什么 BatteryFullLifeTime 未知

如果你反汇编GetSystemPowerStatusWin7 64 位的函数(你可以在这里找到我的反汇编)并重写一个等效的 C 程序,你会得到这样的东西

@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;
}
Run Code Online (Sandbox Code Playgroud)

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


较新的版本

您可以拨打CallNtPowerInformationPowrProf.dll获得关于电池状态更可靠的信息。

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

职业教授Java

 public SYSTEM_POWER_STATUS()
 {
    setAlignType(ALIGN_MSVC);
 }
Run Code Online (Sandbox Code Playgroud)

以及它的用途

    00 08 5f 00 96 30 00 00 ff ff ff ff
    ¯¯ ¯¯ ¯¯ ¯¯ ¯¯¯¯¯¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯¯¯¯
     |  | |  |          |             |   
     |  | |  |       BatteryLifeTime  |
     |  | | Reserved1                 |
     |  | |                      BatteryFullLifeTime     
     |  | BatteryLifePercent
     |  |
     | BatteryFlags
     |
 AcLineStatus
Run Code Online (Sandbox Code Playgroud)

放电时样品输出:

交流线?false
电池存在吗?true
电池流量:放电
最大容量 (mWh):35090
当前充电 (mWh):34160
流量 (mW/s):-11234
预计时间(来自操作系统):10940
预计时间(手动):10946
预计充满时间(手动)):-1
当前电量(百分比):97.34
当前电量(积分百分比):98

充电时输出示例:

交流线?true
电池存在吗?true
电池流量:充电
最大容量 (mWh):35090
当前充电 (mWh):33710
流量 (mW/s):3529
预计时间(来自操作系统):-1
预计时间(手动):-1
预计充满时间(手册):1407 当前电量(百分比):96.06
当前电量(积分百分比):97


NB插拔电源线进行测试时,请稍等片刻,因为监控不是实时的。

PS
我用化名Mijo签署我的代码,您可以删除该评论。