Windows Kernel32.BatteryLifePercent = 255

我试图build立一个Java应用程序,读取笔记本电脑电池的状态,并发送通知给用户,如果它低。 为了做到这一点,我正在使用与Kernel32本机库的Jna,这个问题的第一个答案中解释: 如何获得在Windows系统中的剩余电池寿命?

运行这个例子,程序产生这个输出:

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

Kernel32 BatteryLifePercentBatteryLifeTime值分别为255(未知)和0(我没有得到这个值,未知值为-1,根据微软文档在这里: https : //msdn.microsoft .com / en-us / library / windows / desktop / aa373232(v = vs.85).aspx )。

我的问题是:为什么我得到这些值? Windows电池托盘图标显示正确的百分比,那么为什么我不能从这里得到这些数据?

我正在运行Windows 7旗舰版64位。

谢谢。

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

字段以错误的方式排序,使用 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是用微软编译器编译的,它将数据放在自然边界上。

换句话说,结构是自然对齐的设计,所以它不需要特定的对齐约束


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

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 

根据原始代码的字段顺序,字段是如何被初始化的

  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 (你可以在这里找到我的反汇编),并重写一个等价的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; } 

这显示从未从SYSTEM_BATTERY_STATE结构复制SYSTEM_BATTERY_STATE 。 它总是-1。
另外,值4(临界电池电量)的标志从不设置。
在较新版本的Windows中,这些可能已经被修复了。


更新的版本

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

如果您不熟悉访问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); } 

放电时的样品输出:

交流线? 假
电池存在? 真正
电池流量:放电
最大容量(mWh):35090
当前充电(mWh):34160
流量(mW / s):-11234
预计时间(从操作系统):10940
预计时间(手动):10946
预计完成时间(手动):-1
当前收费(百分比):97.34
当前收费(积分百分比):98

充电时的示例输出:

交流线? 真正
电池存在? 真正
电池流量:充电
最大容量(mWh):35090
当前收费(mWh):33710
流量(mW / s):3529
预计时间(从操作系统):-1
预计时间(手动):-1
预计完全时间(手动):1407当前收费(百分比):96.06
当前收费(积分百分比):97


注意插拔电源线时,请等待几秒,因为监控不是实时的。

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