-
-
Save ahawker/9715872 to your computer and use it in GitHub Desktop.
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; | |
// } |
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.
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.
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?).
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.
@StevenJDH, would be nice if you post a link to your forked gist.
@daluu sure, here it is: https://gist.github.com/StevenJDH/c66655967e21072b1d8644456129efc0
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
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);
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.
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!