我正在尝试构建一个读取笔记本电脑电池状态的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 值中读取,这些值为255(未知)和0(我没有得到此值根据Microsoft文档,Unknown将为-1: https://msdn.microsoft.com/en-us/library/windows/desktop/aa373232(v=vs.85).aspx)。
我的问题是:为什么我会收回这些价值? Windows电池托盘图标显示正确的百分比,为什么我无法从此处获取数据?
我正在运行Windows 7 Ultimate Edition 64位。
谢谢。
答案 0 :(得分:4)
链接答案中的代码错误(编辑:现在 这是固定的)。
字段按错误方式排序,使用
更改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:离线
电池标志:高,超过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
缺口是由于默认对齐方式将数据对齐在自然边界上 由于这些字段已经重新排序,因此它们不再处于原始位置并且是连续的。
如果您为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中,这些可能已被修复。
您可以致电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);
}
放电时的样品输出:
AC线?假
电池存在?真正
电池流量:放电
最大容量(mWh):35090
当前费用(mWh):34160
流量(mW / s): - 12034
预计时间(来自OS):10940
预计时间(手动):10946
预计完成时间(手动):-1
当前费用(百分比):97.34
电流充电(积分百分比):98
充电时的样品输出:
AC线?真正
电池存在?真正
电池流量:充电
最大容量(mWh):35090
当前费用(mWh):33710
流量(mW / s):3529
预计时间(来自OS): - 1 预计时间(手动): - 1 预计完成时间(手动):1407 当前费用(百分比):96.06
当前费用(整数百分比):97
N.B。当插拔电源线进行测试时,请等待几秒钟,因为监控不是实时的。
P.S。
我使用假名 Mijo 签署我的代码,您可以删除该评论。