Получение текущего состояния питания USB
Я пытался прочитать текущее состояние питания порта USB (D0/D1/D2/D3). Я не смог найти много информации о том, как получить доступ к фактическому состоянию. Вот описание состояний питания USB-устройств в документах Microsoft. В нем есть целый раздел об изменениях, но на самом деле не понимаю, как его читать. У меня очень мало опыта работы на Windows и на аппаратном уровне, поэтому извините, если это очевидно.
Я также нашел это отладочное приложение Microsoft, написанное на C, под названием USBView. Если вы установите его и откроете дерево USB, первой информацией, отображаемой для отдельных портов, будет состояние питания.
например
Device Power State: PowerDeviceD2
У него есть источник, доступный на GitHub, но файлы имеют длину более 5000 строк, и я не могу достаточно хорошо ориентироваться в коде C, чтобы сказать, как на самом деле читать состояние питания.
Я пытаюсь реализовать это в приложении C#, но помощь на любом языке будет оценена по достоинству!
1 ответ
После множества копаний в исходном коде USBView я понял, что вам нужно сделать следующее:
Получить указатель на информацию об устройстве, используя
IntPtr deviceInfoSet = SetupDiGetClassDevs(ref classGuid, null, IntPtr.Zero, 0x00000002 | 0x00000010)
где
classGuid
для USB устройств есть"A5DCBF10-6530-11D2-901F-00C04FB951ED"
Получить информацию об отдельном устройстве, используя
SetupDiEnumDeviceInfo(deviceInfoSet, index, ref deviceInfoData)
где
deviceInfoData
является примеромSP_DEVINFO_DATA
структура со значениемcbSize
inicializace to 28. (Информация об устройстве будет сохранена в этой структуре.) Вы начинаете сindex = 0
и затем увеличивать, пока метод не вернетсяfalse
а такжеMarshal.GetLastWin32Error()
возвращает 259 (ERROR_NO_MORE_ITEMS
) чтобы получить все устройства.Затем вы получаете свойство power, используя
SetupDiGetDeviceRegistryProperty
,Вы также можете:
А. пройти в
CM_POWER_DATA
(тогда вы должны заменитьbyte[]
в сигнатуре метода DllImport сref CM_POWER_DATA
)Б. передать байтовый массив, а затем проанализировать байтовый массив в
CM_POWER_DATA
(код из этого ответа оказался очень хорошим в этом).
Здесь я показываю вариант B - передача в байтовом массиве. Данные о мощности будут в data
переменная. (Преобразование пропущено.)
//sizeof evaluates to 56, if you want to hardcode it
byte[] data = new byte[Marshal.SizeOf<CM_POWER_DATA>()];
SetupDiGetDeviceRegistryProperty(
deviceInfoSet,
ref deviceInfoData,
0x0000001E, //the property SPDRP_DEVICE_POWER_DATA
out uint type,
data,
data.Length,
out uint size)
);
Затем вы можете запросить дополнительную информацию с помощью SetupDiGetDeviceRegistryProperty
функции, такие как HardwareID и описание.
Вот мой код
Не забывай
using System.Runtime.InteropServices;
Константы различных GUID и свойств
public const uint SPDRP_DEVICEDESC = (0x00000000); // DeviceDesc (R/W)
public const uint SPDRP_HARDWAREID = (0x00000001); // HardwareID (R/W)
public const uint SPDRP_COMPATIBLEIDS = (0x00000002); // CompatibleIDs (R/W)
public const uint SPDRP_UNUSED0 = (0x00000003); // unused
public const uint SPDRP_SERVICE = (0x00000004); // Service (R/W)
public const uint SPDRP_UNUSED1 = (0x00000005); // unused
public const uint SPDRP_UNUSED2 = (0x00000006); // unused
public const uint SPDRP_CLASS = (0x00000007); // Class (R--tied to ClassGUID)
public const uint SPDRP_CLASSGUID = (0x00000008); // ClassGUID (R/W)
public const uint SPDRP_DRIVER = (0x00000009); // Driver (R/W)
public const uint SPDRP_CONFIGFLAGS = (0x0000000A); // ConfigFlags (R/W)
public const uint SPDRP_MFG = (0x0000000B); // Mfg (R/W)
public const uint SPDRP_FRIENDLYNAME = (0x0000000C); // FriendlyName (R/W)
public const uint SPDRP_LOCATION_INFORMATION = (0x0000000D); // LocationInformation (R/W)
public const uint SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = (0x0000000E); // PhysicalDeviceObjectName (R)
public const uint SPDRP_CAPABILITIES = (0x0000000F); // Capabilities (R)
public const uint SPDRP_UI_NUMBER = (0x00000010); // UiNumber (R)
public const uint SPDRP_UPPERFILTERS = (0x00000011); // UpperFilters (R/W)
public const uint SPDRP_LOWERFILTERS = (0x00000012); // LowerFilters (R/W)
public const uint SPDRP_BUSTYPEGUID = (0x00000013); // BusTypeGUID (R)
public const uint SPDRP_LEGACYBUSTYPE = (0x00000014); // LegacyBusType (R)
public const uint SPDRP_BUSNUMBER = (0x00000015); // BusNumber (R)
public const uint SPDRP_ENUMERATOR_NAME = (0x00000016); // Enumerator Name (R)
public const uint SPDRP_SECURITY = (0x00000017); // Security (R/W, binary form)
public const uint SPDRP_SECURITY_SDS = (0x00000018); // Security (W, SDS form)
public const uint SPDRP_DEVTYPE = (0x00000019); // Device Type (R/W)
public const uint SPDRP_EXCLUSIVE = (0x0000001A); // Device is exclusive-access (R/W)
public const uint SPDRP_CHARACTERISTICS = (0x0000001B); // Device Characteristics (R/W)
public const uint SPDRP_ADDRESS = (0x0000001C); // Device Address (R)
public const uint SPDRP_UI_NUMBER_DESC_FORMAT = (0X0000001D); // UiNumberDescFormat (R/W)
public const uint SPDRP_DEVICE_POWER_DATA = (0x0000001E); // Device Power Data (R)
public const uint SPDRP_REMOVAL_POLICY = (0x0000001F); // Removal Policy (R)
public const uint SPDRP_REMOVAL_POLICY_HW_DEFAULT = (0x00000020); // Hardware Removal Policy (R)
public const uint SPDRP_REMOVAL_POLICY_OVERRIDE = (0x00000021); // Removal Policy Override (RW)
public const uint SPDRP_INSTALL_STATE = (0x00000022); // Device Install State (R)
public const uint SPDRP_LOCATION_PATHS = (0x00000023); // Device Location Paths (R)
public const uint SPDRP_BASE_CONTAINERID = (0x00000024); // Base ContainerID (R)
public const uint SPDRP_MAXIMUM_PROPERTY = (0x00000025); // Upper bound on ordinals
public const string GUID_DEVINTERFACE_USB_HUB = "f18a0e88-c30c-11d0-8815-00a0c906bed8";
public const string GUID_DEVINTERFACE_USB_DEVICE = "A5DCBF10-6530-11D2-901F-00C04FB951ED";
public const string GUID_DEVINTERFACE_USB_HOST_CONTROLLER = "3ABF6F2D-71C4-462a-8A92-1E6861E6AF27";
public const string GUID_USB_WMI_STD_DATA = "4E623B20-CB14-11D1-B331-00A0C959BBD2";
public const string GUID_USB_WMI_STD_NOTIFICATION = "4E623B20-CB14-11D1-B331-00A0C959BBD2";
public const string GUID_USB_WMI_DEVICE_PERF_INFO = "66C1AA3C-499F-49a0-A9A5-61E2359F6407";
public const string GUID_USB_WMI_NODE_INFO = "{9C179357-DC7A-4f41-B66B-323B9DDCB5B1}";
public const string GUID_USB_WMI_TRACING = "3a61881b-b4e6-4bf9-ae0f-3cd8f394e52f";
public const string GUID_USB_TRANSFER_TRACING = "{681EB8AA-403D-452c-9F8A-F0616FAC9540}";
public const string GUID_USB_PERFORMANCE_TRACING = "{D5DE77A6-6AE9-425c-B1E2-F5615FD348A9}";
public const string GUID_USB_WMI_SURPRISE_REMOVAL_NOTIFICATION = "{9BBBF831-A2F2-43B4-96D1-86944B5914B3}";
Структуры и перечисления:
public enum DEVICE_POWER_STATE {
PowerDeviceUnspecified,
PowerDeviceD0,
PowerDeviceD1,
PowerDeviceD2,
PowerDeviceD3,
PowerDeviceMaximum
}
public enum SYSTEM_POWER_STATE {
PowerSystemUnspecified,
PowerSystemWorking,
PowerSystemSleeping1,
PowerSystemSleeping2,
PowerSystemSleeping3,
PowerSystemHibernate,
PowerSystemShutdown,
PowerSystemMaximum
}
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct SP_DEVINFO_DATA {
public UInt32 cbSize;
public Guid ClassGuid;
public UInt32 DevInst;
public IntPtr Reserved;
}
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct CM_POWER_DATA {
public uint PD_Size;
public DEVICE_POWER_STATE PD_MostRecentPowerState;
public uint PD_Capabilities;
public uint PD_D1Latency;
public uint PD_D2Latency;
public uint PD_D3Latency;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)]
public DEVICE_POWER_STATE[] PD_PowerStateMapping;
public SYSTEM_POWER_STATE PD_DeepestSystemWake;
}
DllImports
[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool SetupDiGetDeviceRegistryPropertyA(
IntPtr DeviceInfoSet,
ref SP_DEVINFO_DATA DeviceInfoData,
uint Property,
out RegistryDataType PropertyRegDataType,
byte[] PropertyBuffer,
//ref CM_POWER_DATA PropertyBuffer,
uint PropertyBufferSize,
out UInt32 RequiredSize
);
[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetupDiGetClassDevsA(
ref Guid ClassGuid,
[MarshalAs(UnmanagedType.LPTStr)] string Enumerator,
IntPtr hwndParent,
uint Flags
);
[DllImport("setupapi.dll", SetLastError=true)]
private static extern bool SetupDiEnumDeviceInfo(
IntPtr DeviceInfoSet,
uint MemberIndex,
ref SP_DEVINFO_DATA DeviceInfoData
);
Различные функции, позволяющие сделать поиск более изящным и простым в использовании. Основной и тот, который я описал выше, является первым - GetInfoWithSetupDi
, (Вам нужно передать ему GUID_DEVINTERFACE_USB_DEVICE.)
/// <summary>
/// Gets information about devices in the device class including power state.
/// </summary>
/// <param name="classGuid">The GUID of the class in which to get information about devices from.</param>
/// <returns></returns>
public static List<DeviceInfo> GetInfoWithSetupDi(Guid classGuid) {
List<DeviceInfo> deviceInfos = new List<DeviceInfo>();
IntPtr deviceInfoSet = SetupDiGetClassDevsA(ref classGuid, null, IntPtr.Zero, 0x00000002 | 0x00000010);
uint index = 0;
int error = 0;
while (error == 0) {
DeviceInfo curDevice = new DeviceInfo {
//Initializing SP_DEVINFO_DATA to be passed to SetupDiEnumDeviceInfo.
deviceInfoData = new SP_DEVINFO_DATA {
cbSize = 28
}
};
//Retrieves the information about the specified device.
bool success = SetupDiEnumDeviceInfo(deviceInfoSet, index, ref curDevice.deviceInfoData);
index++;
error = Marshal.GetLastWin32Error();
if (error == 259)
{ break; }
if (!success)
{ throw new Exception("Native method call error: " + error.ToString()); }
//Only add device after it was at least successfully retrieved.
deviceInfos.Add(curDevice);
//Retrieving individual information.
RegistryData localGetData(uint property) => GetData(deviceInfoSet, curDevice.deviceInfoData, property);
curDevice.hardwareId = (string[]) localGetData(SPDRP_HARDWAREID).parsed;
try {
curDevice.description = (string)localGetData(SPDRP_DEVICEDESC).parsed;
} catch {
curDevice.description = "Description not set.";
}
curDevice.cmPowerData = MarshallingUtils.FromBytes<CM_POWER_DATA>(
localGetData(SPDRP_DEVICE_POWER_DATA).data
);
}
return deviceInfos;
}
/// <summary>
/// Gets the required size in bytes for the given property.
/// </summary>
/// <param name="deviceInfoSet">A handle to the set of devices.</param>
/// <param name="property">The property for which the get the required size.</param>
private static uint GetRequiredSize(IntPtr deviceInfoSet, uint Property) {
SP_DEVINFO_DATA deviceInfoData = new SP_DEVINFO_DATA {
cbSize = 28
};
ThrowErrorIfNotSuccessful(SetupDiEnumDeviceInfo(deviceInfoSet, 0, ref deviceInfoData));
return GetRequiredSize(deviceInfoSet, Property, deviceInfoData);
}
/// <summary>
/// Gets the required size in bytes for the given property.
/// </summary>
/// <param name="deviceInfoSet">A handle to the set of devices.</param>
/// <param name="property">The property for which the get the required size.</param>
/// <param name="deviceInfoData">Info of a device.</param>
/// <returns></returns>
private static uint GetRequiredSize(IntPtr deviceInfoSet, uint property, SP_DEVINFO_DATA deviceInfoData) {
ThrowErrorIfNotSuccessful(
SetupDiGetDeviceRegistryPropertyA(
deviceInfoSet,
ref deviceInfoData,
property,
out RegistryDataType type,
new byte[1000],
1000,
out uint size)
);
return size;
}
/// <summary>
/// Gets the property using SetupDi of the device on the given index in the given device class.
/// </summary>
/// <param name="classGuid">The GUID of the device class.</param>
/// <param name="index">The index of the device.</param>
/// <param name="property">The property to retrieve.</param>
/// <returns></returns>
public static RegistryData GetData(Guid classGuid, uint index, uint property) {
IntPtr deviceInfoSet = SetupDiGetClassDevsA(ref classGuid, null, IntPtr.Zero, 0x00000002 | 0x00000010);
SP_DEVINFO_DATA deviceInfoData = new SP_DEVINFO_DATA {
cbSize = 28
};
ThrowErrorIfNotSuccessful(SetupDiEnumDeviceInfo(deviceInfoSet, index, ref deviceInfoData));
return GetData(deviceInfoSet, deviceInfoData, property);
}
/// <summary>
/// Gets the specified property using SetupDi of the device described in deviceInfoData in the given device info set.
/// The size is retrieved automatically.
/// </summary>
/// <param name="deviceInfoSet">A handle to the device info set.</param>
/// <param name="deviceInfoData">Description of the device.</param>
/// <param name="property">The property to retrieve.</param>
/// <returns></returns>
public static RegistryData GetData(IntPtr deviceInfoSet, SP_DEVINFO_DATA deviceInfoData, uint property) {
uint size = GetRequiredSize(deviceInfoSet, property, deviceInfoData);
return GetData(deviceInfoSet, deviceInfoData, property, size);
}
/// <summary>
/// Gets the specified property using SetupDi of the device described in deviceInfoData in the given device info set.
/// </summary>
/// <param name="deviceInfoSet">A handle to the device info set.</param>
/// <param name="deviceInfoData">Description of the device.</param>
/// <param name="property">The property to retrieve.</param>
/// <returns></returns>
public static RegistryData GetData(IntPtr deviceInfoSet, SP_DEVINFO_DATA deviceInfoData, uint property, uint size) {
byte[] data = new byte[size];
ThrowErrorIfNotSuccessful(
SetupDiGetDeviceRegistryPropertyA(
deviceInfoSet,
ref deviceInfoData,
property,
out RegistryDataType type,
data,
size,
out uint dummysize)
);
return new RegistryData(type, data);
}
/// <summary>
/// Method used to wrap native DLL calls. Throws the last system error when the call is unsuccessful.
/// </summary>
/// <param name="success">Return value of the native method. ( Usually used as ThrowErrorIfNotSuccessful(MethodCall()); )</param>
private static void ThrowErrorIfNotSuccessful(bool success) {
if (!success) {
throw new Exception("Native method call error: " + Marshal.GetLastWin32Error().ToString());
}
}
Общая версия синтаксического анализатора байтового массива <->, упомянутого выше.
public static class MarshallingUtils {
public static byte[] GetBytes<T>(T str) where T : struct {
int size = Marshal.SizeOf(str);
byte[] arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(str, ptr, true);
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);
return arr;
}
public static T FromBytes<T>(byte[] arr) where T : struct {
T str = new T();
int size = Marshal.SizeOf(str);
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.Copy(arr, 0, ptr, size);
str = (T)Marshal.PtrToStructure(ptr, str.GetType());
Marshal.FreeHGlobal(ptr);
return str;
}
}
Вспомогательный класс для анализа полученных данных реестра.
public struct RegistryData {
public readonly RegistryDataType type;
public readonly byte[] data;
public readonly object parsed;
public RegistryData(RegistryDataType type, byte[] data) {
this.type = type;
this.data = data;
this.parsed = ParseData(type, data);
}
public static string ParseString(byte[] data) {
string s = "";
foreach (byte b in data) {
if (b == 0) {
break;
}
s += (char)b;
}
return s;
}
public static string[] ParseMultiString(byte[] data) {
List<string> list = new List<string>();
string current = "";
bool terminator = false;
foreach (byte b in data) {
if (b == 0) {
if (terminator) {
break;
}
else {
terminator = true;
//Start a new string.
list.Add(current);
current = "";
}
}
else {
terminator = false;
current += (char)b;
}
}
return list.ToArray();
}
private static object ParseData(RegistryDataType type, byte[] data) {
switch (type) {
case RegistryDataType.REG_SZ:
return (object)ParseString(data);
case RegistryDataType.REG_MULTI_SZ:
return (object)ParseMultiString(data);
default: return null;
}
}
public override string ToString() {
switch (type) {
case RegistryDataType.REG_SZ: return (string)parsed;
case RegistryDataType.REG_MULTI_SZ: return String.Join(";", (List<string>)parsed);
default: return null;
}
}
}
И последнее перечисление для извлеченного типа данных реестра.
public enum RegistryDataType {
REG_NONE = (0), // No value type
REG_SZ = (1), // Unicode nul terminated string
REG_EXPAND_SZ = (2), // Unicode nul terminated string
// = (with environment variable references)
REG_BINARY = (3), // Free form binary
//REG_DWORD = (4), // 32-bit number
REG_DWORD_LITTLE_ENDIAN = (4), // 32-bit number = (same as REG_DWORD)
REG_DWORD_BIG_ENDIAN = (5), // 32-bit number
REG_LINK = (6), // Symbolic Link = (unicode)
REG_MULTI_SZ = (7), // Multiple Unicode strings
REG_RESOURCE_LIST = (8), // Resource list in the resource map
REG_FULL_RESOURCE_DESCRIPTOR = (9), // Resource list in the hardware description
REG_RESOURCE_REQUIREMENTS_LIST = (10),
//REG_QWORD = (11), // 64-bit number
REG_QWORD_LITTLE_ENDIAN = (11), // 64-bit number = (same as REG_QWORD)
}
А также класс для хранения информации.
public class DeviceInfo {
public SP_DEVINFO_DATA deviceInfoData;
public string[] hardwareId;
public string description;
public CM_POWER_DATA cmPowerData;
}