Skip to content

Instantly share code, notes, and snippets.

@ahawker
Last active February 16, 2024 06:36
Show Gist options
  • Star 18 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save ahawker/9715872 to your computer and use it in GitHub Desktop.
Save ahawker/9715872 to your computer and use it in GitHub Desktop.
Battery info from win32 api
using System;
using System.IO;
using Microsoft.Win32.SafeHandles;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;
namespace Test
{
class Program
{
static void Main(string[] args)
{
BatteryInformation cap = BatteryInfo.GetBatteryInformation();
}
}
public class BatteryInformation
{
public uint CurrentCapacity { get; set; }
public int DesignedMaxCapacity { get; set; }
public int FullChargeCapacity { get; set; }
public uint Voltage { get; set; }
public int DischargeRate { get; set; }
}
public static class BatteryInfo
{
public static BatteryInformation GetBatteryInformation()
{
IntPtr deviceDataPointer = IntPtr.Zero;
IntPtr queryInfoPointer = IntPtr.Zero;
IntPtr batteryInfoPointer = IntPtr.Zero;
IntPtr batteryWaitStatusPointer = IntPtr.Zero;
IntPtr batteryStatusPointer = IntPtr.Zero;
try
{
IntPtr deviceHandle = SetupDiGetClassDevs(
Win32.GUID_DEVCLASS_BATTERY, Win32.DEVICE_GET_CLASS_FLAGS.DIGCF_PRESENT | Win32.DEVICE_GET_CLASS_FLAGS.DIGCF_DEVICEINTERFACE);
Win32.SP_DEVICE_INTERFACE_DATA deviceInterfaceData = new Win32.SP_DEVICE_INTERFACE_DATA();
deviceInterfaceData.CbSize = Marshal.SizeOf(deviceInterfaceData);
SetupDiEnumDeviceInterfaces(deviceHandle, Win32.GUID_DEVCLASS_BATTERY, 0, ref deviceInterfaceData);
deviceDataPointer = Marshal.AllocHGlobal(Win32.DEVICE_INTERFACE_BUFFER_SIZE);
//Win32.SP_DEVICE_INTERFACE_DETAIL_DATA deviceDetailData =
// (Win32.SP_DEVICE_INTERFACE_DETAIL_DATA)Marshal.PtrToStructure(deviceDataPointer, typeof(Win32.SP_DEVICE_INTERFACE_DETAIL_DATA));
//toggle these two and see if naything changes... ^^^^^^^^^^^^
Win32.SP_DEVICE_INTERFACE_DETAIL_DATA deviceDetailData = new Win32.SP_DEVICE_INTERFACE_DETAIL_DATA();
deviceDetailData.CbSize = (IntPtr.Size == 8) ? 8 : 4 + Marshal.SystemDefaultCharSize;
SetupDiGetDeviceInterfaceDetail(deviceHandle, ref deviceInterfaceData, ref deviceDetailData, Win32.DEVICE_INTERFACE_BUFFER_SIZE);
IntPtr batteryHandle = CreateFile(deviceDetailData.DevicePath, FileAccess.ReadWrite, FileShare.ReadWrite, FileMode.Open, Win32.FILE_ATTRIBUTES.Normal);
Win32.BATTERY_QUERY_INFORMATION queryInformation = new Win32.BATTERY_QUERY_INFORMATION();
DeviceIoControl(batteryHandle, Win32.IOCTL_BATTERY_QUERY_TAG, ref queryInformation.BatteryTag);
Win32.BATTERY_INFORMATION batteryInformation = new Win32.BATTERY_INFORMATION();
queryInformation.InformationLevel = Win32.BATTERY_QUERY_INFORMATION_LEVEL.BatteryInformation;
int queryInfoSize = Marshal.SizeOf(queryInformation);
int batteryInfoSize = Marshal.SizeOf(batteryInformation);
queryInfoPointer = Marshal.AllocHGlobal(queryInfoSize);
Marshal.StructureToPtr(queryInformation, queryInfoPointer, false);
batteryInfoPointer = Marshal.AllocHGlobal(batteryInfoSize);
Marshal.StructureToPtr(batteryInformation, batteryInfoPointer, false);
DeviceIoControl(batteryHandle, Win32.IOCTL_BATTERY_QUERY_INFORMATION, queryInfoPointer, queryInfoSize, batteryInfoPointer, batteryInfoSize);
Win32.BATTERY_INFORMATION updatedBatteryInformation =
(Win32.BATTERY_INFORMATION)Marshal.PtrToStructure(batteryInfoPointer, typeof(Win32.BATTERY_INFORMATION));
Win32.BATTERY_WAIT_STATUS batteryWaitStatus = new Win32.BATTERY_WAIT_STATUS();
batteryWaitStatus.BatteryTag = queryInformation.BatteryTag;
Win32.BATTERY_STATUS batteryStatus = new Win32.BATTERY_STATUS();
int waitStatusSize = Marshal.SizeOf(batteryWaitStatus);
int batteryStatusSize = Marshal.SizeOf(batteryStatus);
batteryWaitStatusPointer = Marshal.AllocHGlobal(waitStatusSize);
Marshal.StructureToPtr(batteryWaitStatus, batteryWaitStatusPointer, false);
batteryStatusPointer = Marshal.AllocHGlobal(batteryStatusSize);
Marshal.StructureToPtr(batteryStatus, batteryStatusPointer, false);
DeviceIoControl(batteryHandle, Win32.IOCTL_BATTERY_QUERY_STATUS, batteryWaitStatusPointer, waitStatusSize, batteryStatusPointer, batteryStatusSize);
Win32.BATTERY_STATUS updatedStatus =
(Win32.BATTERY_STATUS)Marshal.PtrToStructure(batteryStatusPointer, typeof(Win32.BATTERY_STATUS));
Win32.SetupDiDestroyDeviceInfoList(deviceHandle);
return new BatteryInformation()
{
DesignedMaxCapacity = updatedBatteryInformation.DesignedCapacity,
FullChargeCapacity = updatedBatteryInformation.FullChargedCapacity,
CurrentCapacity = updatedStatus.Capacity,
Voltage = updatedStatus.Voltage,
DischargeRate = updatedStatus.Rate
};
}
finally
{
Marshal.FreeHGlobal(deviceDataPointer);
Marshal.FreeHGlobal(queryInfoPointer);
Marshal.FreeHGlobal(batteryInfoPointer);
Marshal.FreeHGlobal(batteryStatusPointer);
Marshal.FreeHGlobal(batteryWaitStatusPointer);
}
}
private static bool DeviceIoControl(IntPtr deviceHandle, uint controlCode, ref uint output)
{
uint bytesReturned;
uint junkInput = 0;
bool retval = Win32.DeviceIoControl(
deviceHandle, controlCode, ref junkInput, 0, ref output, (uint)Marshal.SizeOf(output), out bytesReturned, IntPtr.Zero);
if (!retval)
{
int errorCode = Marshal.GetLastWin32Error();
if (errorCode != 0)
throw Marshal.GetExceptionForHR(errorCode);
else
throw new Exception(
"DeviceIoControl call failed but Win32 didn't catch an error.");
}
return retval;
}
private static bool DeviceIoControl(
IntPtr deviceHandle, uint controlCode, IntPtr input, int inputSize, IntPtr output, int outputSize)
{
uint bytesReturned;
bool retval = Win32.DeviceIoControl(
deviceHandle, controlCode, input, (uint)inputSize, output, (uint)outputSize, out bytesReturned, IntPtr.Zero);
if (!retval)
{
int errorCode = Marshal.GetLastWin32Error();
if (errorCode != 0)
throw Marshal.GetExceptionForHR(errorCode);
else
throw new Exception(
"DeviceIoControl call failed but Win32 didn't catch an error.");
}
return retval;
}
private static IntPtr SetupDiGetClassDevs(Guid guid, Win32.DEVICE_GET_CLASS_FLAGS flags)
{
IntPtr handle = Win32.SetupDiGetClassDevs(ref guid, null, IntPtr.Zero, flags);
if (handle == IntPtr.Zero || handle.ToInt32() == -1)
{
int errorCode = Marshal.GetLastWin32Error();
if (errorCode != 0)
throw Marshal.GetExceptionForHR(errorCode);
else
throw new Exception("SetupDiGetClassDev call returned a bad handle.");
}
return handle;
}
private static bool SetupDiEnumDeviceInterfaces(
IntPtr deviceInfoSet, Guid guid, int memberIndex, ref Win32.SP_DEVICE_INTERFACE_DATA deviceInterfaceData)
{
bool retval = Win32.SetupDiEnumDeviceInterfaces(
deviceInfoSet, IntPtr.Zero, ref guid, (uint)memberIndex, ref deviceInterfaceData);
if (!retval)
{
int errorCode = Marshal.GetLastWin32Error();
if (errorCode != 0)
{
if (errorCode == 259)
throw new Exception("SetupDeviceInfoEnumerateDeviceInterfaces ran out of batteries to enumerate.");
throw Marshal.GetExceptionForHR(errorCode);
}
else
throw new Exception(
"SetupDeviceInfoEnumerateDeviceInterfaces call failed but Win32 didn't catch an error.");
}
return retval;
}
private static bool SetupDiDestroyDeviceInfoList(IntPtr deviceInfoSet)
{
bool retval = Win32.SetupDiDestroyDeviceInfoList(deviceInfoSet);
if (!retval)
{
int errorCode = Marshal.GetLastWin32Error();
if (errorCode != 0)
throw Marshal.GetExceptionForHR(errorCode);
else
throw new Exception(
"SetupDiDestroyDeviceInfoList call failed but Win32 didn't catch an error.");
}
return retval;
}
private static bool SetupDiGetDeviceInterfaceDetail(
IntPtr deviceInfoSet, ref Win32.SP_DEVICE_INTERFACE_DATA deviceInterfaceData, ref Win32.SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData, int deviceInterfaceDetailSize)
{
//int tmpSize = Marshal.SizeOf(deviceInterfaceDetailData);
uint reqSize;
bool retval = Win32.SetupDiGetDeviceInterfaceDetail(
deviceInfoSet, ref deviceInterfaceData, ref deviceInterfaceDetailData, (uint)deviceInterfaceDetailSize, out reqSize, IntPtr.Zero);
if (!retval)
{
int errorCode = Marshal.GetLastWin32Error();
if (errorCode != 0)
throw Marshal.GetExceptionForHR(errorCode);
else
throw new Exception(
"SetupDiGetDeviceInterfaceDetail call failed but Win32 didn't catch an error.");
}
return retval;
}
private static IntPtr CreateFile(
string filename, FileAccess access, FileShare shareMode, FileMode creation, Win32.FILE_ATTRIBUTES flags)
{
IntPtr handle = Win32.CreateFile(
filename, access, shareMode, IntPtr.Zero, creation, flags, IntPtr.Zero);
if (handle == IntPtr.Zero || handle.ToInt32() == -1)
{
int errorCode = Marshal.GetLastWin32Error();
if (errorCode != 0)
Marshal.ThrowExceptionForHR(errorCode);
else
throw new Exception(
"SetupDiGetDeviceInterfaceDetail call failed but Win32 didn't catch an error.");
}
return handle;
}
}
internal static class Win32
{
internal static readonly Guid GUID_DEVCLASS_BATTERY = new Guid(0x72631E54, 0x78A4, 0x11D0, 0xBC, 0xF7, 0x00, 0xAA, 0x00, 0xB7, 0xB3, 0x2A);
internal const uint IOCTL_BATTERY_QUERY_TAG = (0x00000029 << 16) | ((int)FileAccess.Read << 14) | (0x10 << 2) | (0);
internal const uint IOCTL_BATTERY_QUERY_INFORMATION = (0x00000029 << 16) | ((int)FileAccess.Read << 14) | (0x11 << 2) | (0);
internal const uint IOCTL_BATTERY_QUERY_STATUS = (0x00000029 << 16) | ((int)FileAccess.Read << 14) | (0x13 << 2) | (0);
internal const int DEVICE_INTERFACE_BUFFER_SIZE = 120;
[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern IntPtr SetupDiGetClassDevs(
ref Guid guid,
[MarshalAs(UnmanagedType.LPTStr)] string enumerator,
IntPtr hwnd,
DEVICE_GET_CLASS_FLAGS flags);
[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool SetupDiDestroyDeviceInfoList(IntPtr deviceInfoSet);
[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool SetupDiEnumDeviceInterfaces(
IntPtr hdevInfo,
IntPtr devInfo,
ref Guid guid,
uint memberIndex,
ref SP_DEVICE_INTERFACE_DATA devInterfaceData);
[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool SetupDiGetDeviceInterfaceDetail(
IntPtr hdevInfo,
ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData,
ref SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData,
uint deviceInterfaceDetailDataSize,
out uint requiredSize,
IntPtr deviceInfoData);
[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool SetupDiGetDeviceInterfaceDetail(
IntPtr hdevInfo,
ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData,
IntPtr deviceInterfaceDetailData,
uint deviceInterfaceDetailDataSize,
out uint requiredSize,
IntPtr deviceInfoData);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern IntPtr CreateFile(
string filename,
[MarshalAs(UnmanagedType.U4)] FileAccess desiredAccess,
[MarshalAs(UnmanagedType.U4)] FileShare shareMode,
IntPtr securityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] FILE_ATTRIBUTES flags,
IntPtr template);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern bool DeviceIoControl(
IntPtr handle,
uint controlCode,
[In] IntPtr inBuffer,
uint inBufferSize,
[Out] IntPtr outBuffer,
uint outBufferSize,
out uint bytesReturned,
IntPtr overlapped);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern bool DeviceIoControl(
IntPtr handle,
uint controlCode,
ref uint inBuffer,
uint inBufferSize,
ref uint outBuffer,
uint outBufferSize,
out uint bytesReturned,
IntPtr overlapped);
[Flags]
internal enum DEVICE_GET_CLASS_FLAGS : uint
{
DIGCF_DEFAULT = 0x00000001,
DIGCF_PRESENT = 0x00000002,
DIGCF_ALLCLASSES = 0x00000004,
DIGCF_PROFILE = 0x00000008,
DIGCF_DEVICEINTERFACE = 0x00000010
}
[Flags]
internal enum LOCAL_MEMORY_FLAGS
{
LMEM_FIXED = 0x0000,
LMEM_MOVEABLE = 0x0002,
LMEM_NOCOMPACT = 0x0010,
LMEM_NODISCARD = 0x0020,
LMEM_ZEROINIT = 0x0040,
LMEM_MODIFY = 0x0080,
LMEM_DISCARDABLE = 0x0F00,
LMEM_VALID_FLAGS = 0x0F72,
LMEM_INVALID_HANDLE = 0x8000,
LHND = (LMEM_MOVEABLE | LMEM_ZEROINIT),
LPTR = (LMEM_FIXED | LMEM_ZEROINIT),
NONZEROLHND = (LMEM_MOVEABLE),
NONZEROLPTR = (LMEM_FIXED)
}
[Flags]
internal enum FILE_ATTRIBUTES : uint
{
Readonly = 0x00000001,
Hidden = 0x00000002,
System = 0x00000004,
Directory = 0x00000010,
Archive = 0x00000020,
Device = 0x00000040,
Normal = 0x00000080,
Temporary = 0x00000100,
SparseFile = 0x00000200,
ReparsePoint = 0x00000400,
Compressed = 0x00000800,
Offline = 0x00001000,
NotContentIndexed = 0x00002000,
Encrypted = 0x00004000,
Write_Through = 0x80000000,
Overlapped = 0x40000000,
NoBuffering = 0x20000000,
RandomAccess = 0x10000000,
SequentialScan = 0x08000000,
DeleteOnClose = 0x04000000,
BackupSemantics = 0x02000000,
PosixSemantics = 0x01000000,
OpenReparsePoint = 0x00200000,
OpenNoRecall = 0x00100000,
FirstPipeInstance = 0x00080000
}
internal enum BATTERY_QUERY_INFORMATION_LEVEL
{
BatteryInformation = 0,
BatteryGranularityInformation = 1,
BatteryTemperature = 2,
BatteryEstimatedTime = 3,
BatteryDeviceName = 4,
BatteryManufactureDate = 5,
BatteryManufactureName = 6,
BatteryUniqueID = 7
}
[Flags]
internal enum POWER_STATE : uint
{
BATTERY_POWER_ONLINE = 0x00000001,
BATTERY_DISCHARGING = 0x00000002,
BATTERY_CHARGING = 0x00000004,
BATTERY_CRITICAL = 0x00000008
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct BATTERY_INFORMATION
{
public int Capabilities;
public byte Technology;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public byte[] Reserved;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] Chemistry;
public int DesignedCapacity;
public int FullChargedCapacity;
public int DefaultAlert1;
public int DefaultAlert2;
public int CriticalBias;
public int CycleCount;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct SP_DEVICE_INTERFACE_DETAIL_DATA
{
public int CbSize;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string DevicePath;
}
[StructLayout(LayoutKind.Sequential)]
internal struct SP_DEVICE_INTERFACE_DATA
{
public int CbSize;
public Guid InterfaceClassGuid;
public int Flags;
public UIntPtr Reserved;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct BATTERY_QUERY_INFORMATION
{
public uint BatteryTag;
public BATTERY_QUERY_INFORMATION_LEVEL InformationLevel;
public int AtRate;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct BATTERY_STATUS
{
public POWER_STATE PowerState;
public uint Capacity;
public uint Voltage;
public int Rate;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct BATTERY_WAIT_STATUS
{
public uint BatteryTag;
public uint Timeout;
public POWER_STATE PowerState;
public uint LowCapacity;
public uint HighCapacity;
}
}
}
// Example from somewhere in MSDN
//
//DWORD GetBatteryState()
// {
//#define GBS_HASBATTERY 0x1
//#define GBS_ONBATTERY 0x2
// // Returned value includes GBS_HASBATTERY if the system has a
// // non-UPS battery, and GBS_ONBATTERY if the system is running on
// // a battery.
// //
// // dwResult & GBS_ONBATTERY means we have not yet found AC power.
// // dwResult & GBS_HASBATTERY means we have found a non-UPS battery.
// DWORD dwResult = GBS_ONBATTERY;
// // IOCTL_BATTERY_QUERY_INFORMATION,
// // enumerate the batteries and ask each one for information.
// HDEVINFO hdev =
// SetupDiGetClassDevs(&GUID_DEVCLASS_BATTERY,
// 0,
// 0,
// DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
// if (INVALID_HANDLE_VALUE != hdev)
// {
// // Limit search to 100 batteries max
// for (int idev = 0; idev < 100; idev++)
// {
// SP_DEVICE_INTERFACE_DATA did = {0};
// did.cbSize = sizeof(did);
// if (SetupDiEnumDeviceInterfaces(hdev,
// 0,
// &GUID_DEVCLASS_BATTERY,
// idev,
// &did))
// {
// DWORD cbRequired = 0;
// SetupDiGetDeviceInterfaceDetail(hdev,
// &did,
// 0,
// 0,
// &cbRequired,
// 0);
// if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
// {
// PSP_DEVICE_INTERFACE_DETAIL_DATA pdidd =
// (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LPTR,
// cbRequired);
// if (pdidd)
// {
// pdidd->cbSize = sizeof(*pdidd);
// if (SetupDiGetDeviceInterfaceDetail(hdev,
// &did,
// pdidd,
// cbRequired,
// &cbRequired,
// 0))
// {
// // Enumerated a battery. Ask it for information.
// HANDLE hBattery =
// CreateFile(pdidd->DevicePath,
// GENERIC_READ | GENERIC_WRITE,
// FILE_SHARE_READ | FILE_SHARE_WRITE,
// NULL,
// OPEN_EXISTING,
// FILE_ATTRIBUTE_NORMAL,
// NULL);
// if (INVALID_HANDLE_VALUE != hBattery)
// {
// // Ask the battery for its tag.
// BATTERY_QUERY_INFORMATION bqi = {0};
// DWORD dwWait = 0;
// DWORD dwOut;
// if (DeviceIoControl(hBattery,
// IOCTL_BATTERY_QUERY_TAG,
// &dwWait,
// sizeof(dwWait),
// &bqi.BatteryTag,
// sizeof(bqi.BatteryTag),
// &dwOut,
// NULL)
// && bqi.BatteryTag)
// {
// // With the tag, you can query the battery info.
// BATTERY_INFORMATION bi = {0};
// bqi.InformationLevel = BatteryInformation;
// if (DeviceIoControl(hBattery,
// IOCTL_BATTERY_QUERY_INFORMATION,
// &bqi,
// sizeof(bqi),
// &bi,
// sizeof(bi),
// &dwOut,
// NULL))
// {
// // Only non-UPS system batteries count
// if (bi.Capabilities & BATTERY_SYSTEM_BATTERY)
// {
// if (!(bi.Capabilities & BATTERY_IS_SHORT_TERM))
// {
// dwResult |= GBS_HASBATTERY;
// }
// // Query the battery status.
// BATTERY_WAIT_STATUS bws = {0};
// bws.BatteryTag = bqi.BatteryTag;
// BATTERY_STATUS bs;
// if (DeviceIoControl(hBattery,
// IOCTL_BATTERY_QUERY_STATUS,
// &bws,
// sizeof(bws),
// &bs,
// sizeof(bs),
// &dwOut,
// NULL))
// {
// if (bs.PowerState & BATTERY_POWER_ON_LINE)
// {
// dwResult &= ~GBS_ONBATTERY;
// }
// }
// }
// }
// }
// CloseHandle(hBattery);
// }
// }
// LocalFree(pdidd);
// }
// }
// }
// else if (ERROR_NO_MORE_ITEMS == GetLastError())
// {
// break; // Enumeration failed - perhaps we're out of items
// }
// }
// SetupDiDestroyDeviceInfoList(hdev);
// }
// // Final cleanup: If we didn't find a battery, then presume that we
// // are on AC power.
// if (!(dwResult & GBS_HASBATTERY))
// dwResult &= ~GBS_ONBATTERY;
// return dwResult;
// }
@eugeniomiro
Copy link

Hi, this is a nice implementation in C# which I tested and works like a charm, but in Windows 10 I had to make a change, in battery.cs line 225, I had to add one more line like this.

retval = Win32.SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref deviceInterfaceData, ref deviceInterfaceDetailData, (uint)reqSize, out reqSize, IntPtr.Zero);

That's because different OS versions have different structure lengths, that's why there's an output parameter which tells you the required size, so the approach would first make a call like a query length then the actual call for the data. With this one, the latest parameter of SetupDiGetDeviceInterfaceDetail loses its meaning...

Hope this helps!

@StevenJDH
Copy link

Great implementation of the BATTERY_INFORMATION and BATTERY_STATUS structures. It was the only method I have seen that actually returns the battery’s cycle count and designed capacity reliably. I have forked your code and made a few changes as follows:

“Fixed arithmetic overflow in 64-bit mode. Exposed cycle count and power state. Changed DiscargeRate to Rate since it also shows charge rate as per MSDN. Other minor corrections made.”

I wish GitHub would implement pull requests for gists. Anyways, thanks a lot.

@LorenzoGodi
Copy link

Hi, first of all thank you for the great code!
I just wanted to warn you that I got overflow errors with this: handle.ToInt32() == -1
I replaced it with handle.ToInt64() == -1 and seems to work fine.

@woelfchen72
Copy link

woelfchen72 commented May 3, 2019

Thank you for this sample code.
I've found out, that not erverytime the memberIndex of der battery is 0. (In my special case I had to use memberIndex 2), so
SetupDiEnumDeviceInterfaces(deviceHandle, Win32.GUID_DEVCLASS_BATTERY, 2, ref deviceInterfaceData); works, all other memberIndex fail on DeviceIoControl(...) later.
I've found an MSDN c++ example here, where they try numbers between 0 and 100 (why 100?).

@woelfchen72
Copy link

woelfchen72 commented May 3, 2019

Nearby,
in cases of failing memberIndices (like I wrote before) in
DeviceIoControl(...)
int errorCode = Marshal.GetLastWin32Error();
if ( errorCode != 0 )
{
throw Marshal.GetExceptionForHR( errorCode );
...

errorCode results to 2,
but then Marshal.GetExceptionForHR( 2 ) returns null.
so throw throws a NullReferenceExeption
Maybe not all Marshal.GetLastWin32Error() -Results map to a corresponding .NET exception.

@daluu
Copy link

daluu commented Jan 28, 2021

@StevenJDH, would be nice if you post a link to your forked gist.

@StevenJDH
Copy link

@SinanAkkoyun
Copy link

OMG I SEARCHED SOO SO LONG FOR A LIVE CHARGE RATE SOLUTION AND HERE WE ARE THANK YOU SO SO MUCH IT JUST WORKS <3 <3 <3 <3

@luojunyuan
Copy link

When I put this to arm64 device. I got errorCode 122 and 1784 (ERROR_INSUFFICIENT_BUFFER) and Object reference not set to an instance of an object from Line223 Win32.SetupDiGetDeviceInterfaceDetail(...

change line 221 -225 to follow works for me. I think this is also the proper way to get the reqSize of deviceInterfaceDetailData

Win32.SetupDiGetDeviceInterfaceDetail(
              deviceInfoSet, ref deviceInterfaceData, IntPtr.Zero, 0,
              out var reqSize, IntPtr.Zero);

            bool retval = Win32.SetupDiGetDeviceInterfaceDetail(
                deviceInfoSet, ref deviceInterfaceData, ref deviceInterfaceDetailData, reqSize,
                out _, IntPtr.Zero);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment