如何通过设备实例路径获取USB设备的位置路径属性
解决方案:通过SetupAPI映射设备实例路径到位置路径
要解决设备实例路径到位置路径的映射问题,直接使用Windows SetupAPI是最可靠的方案——它能直接读取设备管理器中显示的位置路径信息,避开注册表和WMI的局限性。以下是C#的具体实现步骤:
核心思路
通过SetupAPI的一系列函数枚举系统中所有设备,匹配目标设备的实例路径后,读取其DEVPKEY_Device_LocationPaths属性,该属性对应的就是设备管理器中显示的位置路径字符串。
C#实现代码
首先引入必要的P/Invoke声明,再编写匹配逻辑:
using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; public class DeviceLocationHelper { // SetupAPI 函数声明 [DllImport("setupapi.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, string Enumerator, IntPtr hwndParent, uint Flags); [DllImport("setupapi.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet, uint MemberIndex, ref SP_DEVINFO_DATA DeviceInfoData); [DllImport("setupapi.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool SetupDiGetDeviceRegistryProperty(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, uint Property, out uint PropertyRegDataType, IntPtr PropertyBuffer, uint PropertyBufferSize, out uint RequiredSize); [DllImport("setupapi.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool SetupDiGetDeviceRegistryProperty(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, Guid PropertyGuid, uint PropertyPid, out uint PropertyRegDataType, IntPtr PropertyBuffer, uint PropertyBufferSize, out uint RequiredSize); [DllImport("setupapi.dll", SetLastError = true)] private static extern bool SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet); // 结构体定义 [StructLayout(LayoutKind.Sequential)] private struct SP_DEVINFO_DATA { public uint cbSize; public Guid ClassGuid; public uint DevInst; public IntPtr Reserved; } // 属性常量 private const uint DIGCF_ALLCLASSES = 0x00000004; private const uint SPDRP_DEVICE_INSTANCE_PATH = 0x0000001B; private static readonly Guid DEVPKEY_Device_LocationPaths_Fmtid = new Guid("78C34FC8-104A-4ACA-9EA4-524D52996E57"); private const uint DEVPKEY_Device_LocationPaths_Pid = 3; /// <summary> /// 根据设备实例路径获取对应的位置路径 /// </summary> /// <param name="deviceInstancePath">设备实例路径,如USB\VID_0AB4&PID_0010\IG011556</param> /// <returns>位置路径列表,如[PCIROOT(0)#PCI(1D04)#USBROOT(0)#USB(4)]</returns> public static List<string> GetDeviceLocationPaths(string deviceInstancePath) { List<string> locationPaths = new List<string>(); Guid allClassesGuid = Guid.Empty; IntPtr deviceInfoSet = IntPtr.Zero; try { // 获取所有设备的信息集合 deviceInfoSet = SetupDiGetClassDevs(ref allClassesGuid, null, IntPtr.Zero, DIGCF_ALLCLASSES); if (deviceInfoSet == IntPtr.Zero) throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); SP_DEVINFO_DATA deviceInfoData = new SP_DEVINFO_DATA(); deviceInfoData.cbSize = (uint)Marshal.SizeOf(deviceInfoData); // 枚举每个设备 for (uint i = 0; SetupDiEnumDeviceInfo(deviceInfoSet, i, ref deviceInfoData); i++) { // 获取当前设备的实例路径 string currentInstancePath = GetDevicePropertyById(deviceInfoSet, ref deviceInfoData, SPDRP_DEVICE_INSTANCE_PATH); if (string.Equals(currentInstancePath, deviceInstancePath, StringComparison.OrdinalIgnoreCase)) { // 获取位置路径 string paths = GetDevicePropertyByDevPKey(deviceInfoSet, ref deviceInfoData); if (!string.IsNullOrEmpty(paths)) { // 位置路径是多字符串格式,按\0分割 string[] splitPaths = paths.Split(new[] { '\0' }, StringSplitOptions.RemoveEmptyEntries); locationPaths.AddRange(splitPaths); } break; } // 重置结构体大小,避免下一次枚举出错 deviceInfoData.cbSize = (uint)Marshal.SizeOf(deviceInfoData); } int lastError = Marshal.GetLastWin32Error(); if (lastError != 0 && lastError != 259) // 259是枚举结束的正常错误码 throw new System.ComponentModel.Win32Exception(lastError); } finally { // 释放资源 if (deviceInfoSet != IntPtr.Zero) SetupDiDestroyDeviceInfoList(deviceInfoSet); } return locationPaths; } private static string GetDevicePropertyById(IntPtr deviceInfoSet, ref SP_DEVINFO_DATA deviceInfoData, uint propertyId) { uint regDataType; uint requiredSize = 0; // 先获取所需缓冲区大小 if (!SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref deviceInfoData, propertyId, out regDataType, IntPtr.Zero, 0, out requiredSize)) { if (Marshal.GetLastWin32Error() != 122) // 122是缓冲区不足的正常错误码 return null; } IntPtr propertyBuffer = Marshal.AllocHGlobal((int)requiredSize); try { if (SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref deviceInfoData, propertyId, out regDataType, propertyBuffer, requiredSize, out requiredSize)) { if (regDataType == 1) // REG_SZ 或 REG_MULTI_SZ return Marshal.PtrToStringUni(propertyBuffer); } return null; } finally { Marshal.FreeHGlobal(propertyBuffer); } } private static string GetDevicePropertyByDevPKey(IntPtr deviceInfoSet, ref SP_DEVINFO_DATA deviceInfoData) { uint regDataType; uint requiredSize = 0; // 使用SetupDiGetDeviceRegistryProperty的重载获取DEVPKEY属性 if (!SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref deviceInfoData, DEVPKEY_Device_LocationPaths_Fmtid, DEVPKEY_Device_LocationPaths_Pid, out regDataType, IntPtr.Zero, 0, out requiredSize)) { if (Marshal.GetLastWin32Error() != 122) return null; } IntPtr propertyBuffer = Marshal.AllocHGlobal((int)requiredSize); try { if (SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref deviceInfoData, DEVPKEY_Device_LocationPaths_Fmtid, DEVPKEY_Device_LocationPaths_Pid, out regDataType, propertyBuffer, requiredSize, out requiredSize)) { if (regDataType == 1) return Marshal.PtrToStringUni(propertyBuffer); } return null; } finally { Marshal.FreeHGlobal(propertyBuffer); } } } // 调用示例 class Program { static void Main(string[] args) { string deviceInstancePath = "USB\\VID_0AB4&PID_0010\\IG011556"; List<string> locationPaths = DeviceLocationHelper.GetDeviceLocationPaths(deviceInstancePath); foreach (string path in locationPaths) { Console.WriteLine("位置路径: " + path); } } }
关键说明
- 权限要求:程序需要以管理员权限运行,否则SetupAPI可能无法访问部分设备的属性信息。
- 系统兼容性:
DEVPKEY_Device_LocationPaths属性从Windows Vista开始支持,确保运行环境符合要求。 - 多路径处理:部分设备可能有多个位置路径(比如同时支持USB和其他接口),代码中会返回所有路径,可根据实际需求选择合适的条目。
- 错误处理:代码中捕获了Win32错误码,便于调试排查问题。
内容的提问来源于stack exchange,提问作者SourceC0de
相关产品推荐
相关产品推荐

