Windows Kernel32.BatteryLifePercent = 255
Windows Kernel32.BatteryLifePercent = 255
我正在尝试构建一个 Java 应用程序来读取笔记本电脑电池的状态,并在电量不足时向用户发送通知。为了做到这一点,我将 jna 与 Kernel32 本机库一起使用,如本问题的第一个答案中所述:
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 BatteryLifePercent 和 BatteryLifeTime 中读取字段 battery life 和 battery left 的值为 255(未知)和 0(我不要得到这个值。根据此处的 Microsoft 文档,未知将为 -1:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa373232(v=vs.85).aspx).
我的问题是:为什么我要恢复这些值? Windows 电池托盘图标显示了正确的百分比,那么为什么我无法从此处获取该数据?
我是运行Windows7旗舰版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 是用 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 class 可以为您完成工作
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
N.B.插拔电源线测试时,由于监控不是实时的,请稍等片刻。
P.S.
我用化名 Mijo 对我的代码进行签名,您可以删除该注释。
我正在尝试构建一个 Java 应用程序来读取笔记本电脑电池的状态,并在电量不足时向用户发送通知。为了做到这一点,我将 jna 与 Kernel32 本机库一起使用,如本问题的第一个答案中所述: 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 BatteryLifePercent 和 BatteryLifeTime 中读取字段 battery life 和 battery left 的值为 255(未知)和 0(我不要得到这个值。根据此处的 Microsoft 文档,未知将为 -1: https://msdn.microsoft.com/en-us/library/windows/desktop/aa373232(v=vs.85).aspx).
我的问题是:为什么我要恢复这些值? Windows 电池托盘图标显示了正确的百分比,那么为什么我无法从此处获取该数据?
我是运行Windows7旗舰版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 是用 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 class 可以为您完成工作
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
N.B.插拔电源线测试时,由于监控不是实时的,请稍等片刻。
P.S.
我用化名 Mijo 对我的代码进行签名,您可以删除该注释。