11/*
2- * Copyright 2016-2022 The OSHI Project Contributors
2+ * Copyright 2016-2026 The OSHI Project Contributors
33 * SPDX-License-Identifier: MIT
44 */
55package oshi .hardware .platform .windows ;
1414import com .sun .jna .Platform ;
1515import com .sun .jna .platform .win32 .Guid .GUID ;
1616import com .sun .jna .platform .win32 .Kernel32 ;
17- import com .sun .jna .platform .win32 .PowrProf .POWER_INFORMATION_LEVEL ;
1817import com .sun .jna .platform .win32 .SetupApi ;
1918import com .sun .jna .platform .win32 .WinBase ;
2019import com .sun .jna .platform .win32 .WinError ;
2726import oshi .hardware .common .AbstractPowerSource ;
2827import oshi .jna .ByRef .CloseableIntByReference ;
2928import oshi .jna .Struct .CloseableSpDeviceInterfaceData ;
30- import oshi .jna .platform .windows .PowrProf ;
3129import oshi .jna .platform .windows .PowrProf .BATTERY_INFORMATION ;
3230import oshi .jna .platform .windows .PowrProf .BATTERY_MANUFACTURE_DATE ;
3331import oshi .jna .platform .windows .PowrProf .BATTERY_QUERY_INFORMATION ;
3432import oshi .jna .platform .windows .PowrProf .BATTERY_QUERY_INFORMATION_LEVEL ;
3533import oshi .jna .platform .windows .PowrProf .BATTERY_STATUS ;
3634import oshi .jna .platform .windows .PowrProf .BATTERY_WAIT_STATUS ;
37- import oshi .jna .platform .windows .PowrProf .SystemBatteryState ;
3835import oshi .util .Constants ;
3936
4037/**
@@ -102,30 +99,10 @@ private static WindowsPowerSource getPowerSource(String name) {
10299 String psSerialNumber = Constants .UNKNOWN ;
103100 double psTemperature = 0d ;
104101
105- // windows PowerSource information comes from two sources: the PowrProf's
106- // CallNTPowerInformation function which returns information for a single
107- // object, and DeviceIoControl with each battery's (if more than one) handle
108- // (which, in theory, return an array of objects but in most cases should return
109- // one).
102+ // Note: CallNtPowerInformation(SystemBatteryState) was previously used as a
103+ // first pass here but no longer returns accurate data as of Windows 11 23H2.
104+ // All battery information is now sourced exclusively via DeviceIoControl.
110105 //
111- // We start by fetching the PowrProf information, which will be replicated
112- // across all IOCTL entries if there are more than one.
113-
114- try (SystemBatteryState batteryState = new SystemBatteryState ()) {
115- if (0 == PowrProf .INSTANCE .CallNtPowerInformation (POWER_INFORMATION_LEVEL .SystemBatteryState , null , 0 ,
116- batteryState .getPointer (), batteryState .size ()) && batteryState .batteryPresent > 0 ) {
117- if (batteryState .acOnLine == 0 && batteryState .charging == 0 && batteryState .discharging > 0 ) {
118- psTimeRemainingEstimated = batteryState .estimatedTime ;
119- } else if (batteryState .charging > 0 ) {
120- psTimeRemainingEstimated = -2d ;
121- }
122- psMaxCapacity = batteryState .maxCapacity ;
123- psCurrentCapacity = batteryState .remainingCapacity ;
124- psRemainingCapacityPercent = Math .min (1d , (double ) psCurrentCapacity / psMaxCapacity );
125- psPowerUsageRate = batteryState .rate ;
126- }
127- }
128-
129106 // Enumerate batteries and ask each one for information
130107 // Ported from:
131108 // https://docs.microsoft.com/en-us/windows/win32/power/enumerating-battery-devices
@@ -211,6 +188,7 @@ private static WindowsPowerSource getPowerSource(String name) {
211188 }
212189 if (0 != (bs .PowerState & BATTERY_CHARGING )) {
213190 psCharging = true ;
191+ psTimeRemainingEstimated = -2d ;
214192 }
215193 psCurrentCapacity = bs .Capacity ;
216194 psVoltage = bs .Voltage > 0 ? bs .Voltage / 1000d
@@ -219,6 +197,8 @@ private static WindowsPowerSource getPowerSource(String name) {
219197 if (psVoltage > 0 ) {
220198 psAmperage = psPowerUsageRate / psVoltage ;
221199 }
200+ psRemainingCapacityPercent = Math .min (1d ,
201+ (double ) psCurrentCapacity / psMaxCapacity );
222202 }
223203 }
224204
@@ -276,13 +256,16 @@ private static WindowsPowerSource getPowerSource(String name) {
276256 psTimeRemainingInstant = tr .getValue ();
277257 }
278258 }
279- // Fallback
280- if (psTimeRemainingInstant < 0 && psPowerUsageRate != 0 ) {
281- psTimeRemainingInstant = (psMaxCapacity - psCurrentCapacity )
282- * 3600d / psPowerUsageRate ;
283- if (psTimeRemainingInstant < 0 ) {
284- psTimeRemainingInstant *= -1 ;
285- }
259+ // Fallback if BatteryEstimatedTime query failed
260+ if (psTimeRemainingInstant <= 0 && psPowerUsageRate != 0 ) {
261+ psTimeRemainingInstant = psDischarging
262+ ? psCurrentCapacity * 3600d
263+ / Math .abs (psPowerUsageRate )
264+ : (psMaxCapacity - psCurrentCapacity ) * 3600d
265+ / Math .abs (psPowerUsageRate );
266+ }
267+ if (psDischarging && psTimeRemainingInstant > 0 ) {
268+ psTimeRemainingEstimated = psTimeRemainingInstant ;
286269 }
287270 // Exit loop
288271 batteryFound = true ;
0 commit comments