Skip to content

Instantly share code, notes, and snippets.

@ahawker
Last active August 1, 2024 19:24
Show Gist options
  • 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;
// }
@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