private static bool SetupDiGetDeviceInterfaceDetail(IntPtr deviceInfoSet, ref Win32.SP_DEVICE_INTERFACE_DATA deviceInterfaceData, ref Win32.SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData, int deviceInterfaceDetailSize)
        {
            var retval = Win32.SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref deviceInterfaceData, ref deviceInterfaceDetailData, (uint)deviceInterfaceDetailSize, out uint reqSize, IntPtr.Zero);

            retval = Win32.SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref deviceInterfaceData, ref deviceInterfaceDetailData, (uint)reqSize, 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);
        }
        public static BatteryInformation GetBatteryInformation()
        {
            var deviceDataPointer        = IntPtr.Zero;
            var queryInfoPointer         = IntPtr.Zero;
            var batteryInfoPointer       = IntPtr.Zero;
            var batteryWaitStatusPointer = IntPtr.Zero;
            var batteryStatusPointer     = IntPtr.Zero;

            try
            {
                var deviceHandle = SetupDiGetClassDevs(Win32.GUID_DEVCLASS_BATTERY, Win32.DEVICE_GET_CLASS_FLAGS.DIGCF_PRESENT | Win32.DEVICE_GET_CLASS_FLAGS.DIGCF_DEVICEINTERFACE);

                var 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);

                var deviceDetailData = new Win32.SP_DEVICE_INTERFACE_DETAIL_DATA {
                    CbSize = (IntPtr.Size == 8) ? 8 : 4 + Marshal.SystemDefaultCharSize
                };

                SetupDiGetDeviceInterfaceDetail(deviceHandle, ref deviceInterfaceData, ref deviceDetailData, Win32.DEVICE_INTERFACE_BUFFER_SIZE);

                var batteryHandle = CreateFile(deviceDetailData.DevicePath, FileAccess.ReadWrite, FileShare.ReadWrite, FileMode.Open, Win32.FILE_ATTRIBUTES.Normal);

                var queryInformation = new Win32.BATTERY_QUERY_INFORMATION();

                DeviceIoControl(batteryHandle, Win32.IOCTL_BATTERY_QUERY_TAG, ref queryInformation.BatteryTag);

                var batteryInformation = new Win32.BATTERY_INFORMATION();
                queryInformation.InformationLevel = Win32.BATTERY_QUERY_INFORMATION_LEVEL.BatteryInformation;

                var queryInfoSize   = Marshal.SizeOf(queryInformation);
                var 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);

                var updatedBatteryInformation = (Win32.BATTERY_INFORMATION)Marshal.PtrToStructure(batteryInfoPointer, typeof(Win32.BATTERY_INFORMATION));

                var batteryWaitStatus = new Win32.BATTERY_WAIT_STATUS {
                    BatteryTag = queryInformation.BatteryTag
                };

                var batteryStatus = new Win32.BATTERY_STATUS();

                var waitStatusSize    = Marshal.SizeOf(batteryWaitStatus);
                var 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);

                var 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 SetupDiEnumDeviceInterfaces(IntPtr deviceInfoSet, Guid guid, int memberIndex, ref Win32.SP_DEVICE_INTERFACE_DATA deviceInterfaceData)
        {
            var 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);
        }