Skip to content

Instantly share code, notes, and snippets.

@StevenJDH
Forked from ahawker/battery.cs
Created April 8, 2018 11:06
Show Gist options
  • Save StevenJDH/c66655967e21072b1d8644456129efc0 to your computer and use it in GitHub Desktop.
Save StevenJDH/c66655967e21072b1d8644456129efc0 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
{
//BATTERY_INFORMATION
public int DesignedCapacity { get; set; }
public int FullChargeCapacity { get; set; }
public int CycleCount { get; set; }
//BATTERY_STATUS
internal Win32.POWER_STATE PowerState { get; set; }
public uint CurrentCapacity { get; set; }
public uint Voltage { get; set; }
public int Rate { 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 anything 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()
{
DesignedCapacity = updatedBatteryInformation.DesignedCapacity,
FullChargeCapacity = updatedBatteryInformation.FullChargedCapacity,
CycleCount = updatedBatteryInformation.CycleCount,
PowerState = updatedStatus.PowerState,
CurrentCapacity = updatedStatus.Capacity,
Voltage = updatedStatus.Voltage,
Rate = 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.ToInt64() == -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;
// }
@StevenJDH
Copy link
Author

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.

@LunevNF
Copy link

LunevNF commented Oct 25, 2018

Hi. I don't know, but I always see 0 at CycleCount. Do you know, why?

@StevenJDH
Copy link
Author

StevenJDH commented Jan 28, 2021

Not sure, but in building my battery app, I noticed that different systems didn't expose all of the same battery info as others systems or required a different approach, which is delaying my next release. You should test on other computers to see if that's your case. Sorry for the late reply, I rarely check gists.

@JuliaCsharp
Copy link

JuliaCsharp commented Apr 8, 2021

@StevenJDH Greetings! Thank you for the script. It works well. But I got an Exception when reading the Temperature.
In order to read it I changed the line #71 of code <queryInformation.InformationLevel = Win32.BATTERY_QUERY_INFORMATION_LEVEL.BatteryInformation;> for <queryInformation.InformationLevel = Win32.BATTERY_QUERY_INFORMATION_LEVEL.BatteryTemperature;>.
The Exception was thrown by line #161 <throw Marshal.GetExceptionForHR(errorCode);>. And it is System.NullReferenceException: 'Object reference not set to an instance of an object.'

Could anyone help me with this please?
Best

@StevenJDH
Copy link
Author

@JuliaCsharp Glad it was helpful to you. Just having a quick look, I suspect that with your change, you'll likely need to make other changes to accommodate your change. This is my theory anyways since in the code above, the scope of the query was BATTERY_QUERY_INFORMATION_LEVEL.BatteryInformation and now that scope has been narrowed to BATTERY_QUERY_INFORMATION_LEVEL.BatteryTemperature, which is a specific piece of information. I would do a debug to see where and when the object is left null to make more sense of things so that by the time the check for issue happens, we know why the error is thrown. Probably not a helpful answer, but it's been a few years since I've looked at this forked gist. You could also ask the original developer to see if they can give you more clues as to why. I also recommend testing on more than one system system as not all systems from what I have seen expose the same info in the same way.

@S-Ginting
Copy link

Hi, is this class can read charger capability in USB type C PD as well? thank you

@StevenJDH
Copy link
Author

@S-Ginting Assuming I understand your question, the above code currently shows the charge rate, but it shouldn't matter what the source is. I haven't tested with USB-C PD, but in theory, if the focus is on the battery, then any information exposed by the system is available. And the above code is not the only way to get it, but some of the other approaches might require admin level access, which isn't ideal.

@TiranSpierer
Copy link

TiranSpierer commented Nov 30, 2022

@StevenJDH Hi, How can I make this work if I have multiple batteries in my PC? The program only looks at one of them.

EDIT: finding the number of batteries can be done with this line of code:

using System.Management; //if using .NET Core you need to add a nuget called System.Management.
BatteriesCount = new ManagementClass("Win32_Battery").GetInstances().Count;

getting info for each battery can be done by changing this piece of code: (adding batteryIndex argument to GetBatteryInformation, and changing the hardcoded zero to batteryIndex when calling SetupDiEnumDeviceInterfaces)
example: if you have 3 batteries, call this method with batterIndex ranging from 0-2.

public static BatteryInformation GetBatteryInformation(int batteryIndex = 0)
        {
            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, batteryIndex, ref deviceInterfaceData);
                
                //rest of method...

@StevenJDH
Copy link
Author

@TiranSpierer This is a great contribution, thank you. Once I get some time to test, I will include these changes.

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