如何在C#应用中检测设备所连接的是USB2还是USB3端口?
如何在C#应用中检测设备所连接的是USB2还是USB3端口?
我完全懂你的痛点——依赖的设备必须插USB3才能跑满性能,要是用户误插USB2,程序启动时得及时提醒,而且你之前用SetupDi系列API还踩了坑,返回false导致设备列表为空对吧?我来分享两个亲测有效的实现思路,帮你搞定这个问题。
方法一:用WMI快速实现(代码简洁,上手快)
WMI(Windows Management Instrumentation)可以直接查询USB设备的父控制器信息,通过控制器的类型就能判断是USB2还是USB3。这个方法不需要太底层的API调用,适合快速验证需求。
示例代码
using System.Management; public static bool IsDeviceConnectedToUsb3(string deviceNameOrId) { // 查询所有USB设备 var searcher = new ManagementObjectSearcher(@"SELECT * FROM Win32_USBHub"); foreach (ManagementObject device in searcher.Get()) { // 过滤出你的目标设备(可以根据设备名称、硬件ID或者PNP设备ID匹配) if (device["Name"]?.ToString().Contains(deviceNameOrId) == true || device["DeviceID"]?.ToString().Contains(deviceNameOrId) == true) { // 获取父USB控制器 var parentController = new ManagementObject(device["PNPDeviceID"].ToString().Replace("USB\\", "USBROOT\\")); string controllerCaption = parentController["Caption"]?.ToString() ?? ""; // 判断控制器类型:USB3控制器通常包含"USB 3.0"、"xHCI"关键词 if (controllerCaption.Contains("USB 3.0") || controllerCaption.Contains("xHCI")) { return true; } // USB2控制器一般是"USB 2.0"、"EHCI" else if (controllerCaption.Contains("USB 2.0") || controllerCaption.Contains("EHCI")) { return false; } } } // 没找到目标设备 return false; }
注意事项
- 你需要替换
deviceNameOrId为你的设备的名称片段或者PNP设备ID,比如设备管理器里看到的硬件ID。 - 部分系统可能需要程序以管理员权限运行才能正常查询WMI的设备信息。
方法二:修复SetupDi系列API调用(更底层,更准确)
你之前调用SetupDiGetDeviceInterfaceDetail返回false,大概率是因为缓冲区大小未正确分配——这个API需要先调用一次获取所需的缓冲区大小,再分配足够空间后第二次调用才能成功。下面是正确的实现步骤:
核心思路
- 初始化设备接口列表,指定USB设备的GUID。
- 枚举所有USB设备接口。
- 第一次调用
SetupDiGetDeviceInterfaceDetail获取所需的缓冲区大小。 - 分配对应大小的缓冲区,第二次调用获取设备详细信息。
- 通过设备路径调用
DeviceIoControl获取USB设备的连接速度,判断是USB2还是3。
示例代码(关键部分)
using System; using System.Runtime.InteropServices; using System.Text; public class UsbPortDetector { // USB设备的GUID private static readonly Guid UsbDeviceGuid = Guid.Parse("{A5DCBF10-6530-11D2-901F-00C04FB951ED}"); [DllImport("setupapi.dll", SetLastError = true)] private static extern bool SetupDiGetClassDevs(ref Guid ClassGuid, uint Enumerator, IntPtr hwndParent, uint Flags); [DllImport("setupapi.dll", SetLastError = true)] private static extern bool SetupDiEnumDeviceInterfaces(IntPtr hDevInfo, IntPtr devInfoData, ref Guid interfaceClassGuid, uint memberIndex, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData); [DllImport("setupapi.dll", SetLastError = true)] private static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr hDevInfo, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, IntPtr deviceInterfaceDetailData, uint deviceInterfaceDetailDataSize, out uint requiredSize, IntPtr deviceInfoData); // 定义所需的结构体 [StructLayout(LayoutKind.Sequential)] private struct SP_DEVICE_INTERFACE_DATA { public int cbSize; public Guid InterfaceClassGuid; public int Flags; public IntPtr Reserved; } public static bool IsDeviceOnUsb3Port(string targetDeviceId) { IntPtr hDevInfo = SetupDiGetClassDevs(ref UsbDeviceGuid, 0, IntPtr.Zero, 0x00000001); // DIGCF_PRESENT if (hDevInfo == IntPtr.Zero) { // 处理错误,比如调用Marshal.GetLastWin32Error()查看错误码 return false; } SP_DEVICE_INTERFACE_DATA deviceInterfaceData = new SP_DEVICE_INTERFACE_DATA(); deviceInterfaceData.cbSize = Marshal.SizeOf(deviceInterfaceData); uint index = 0; while (SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, ref UsbDeviceGuid, index, ref deviceInterfaceData)) { // 第一次调用获取所需缓冲区大小 uint requiredSize = 0; SetupDiGetDeviceInterfaceDetail(hDevInfo, ref deviceInterfaceData, IntPtr.Zero, 0, out requiredSize, IntPtr.Zero); // 分配缓冲区 IntPtr detailDataPtr = Marshal.AllocHGlobal((int)requiredSize); Marshal.WriteInt32(detailDataPtr, (IntPtr.Size == 8) ? 8 : 4); // 设置SP_DEVICE_INTERFACE_DETAIL_DATA的cbSize if (SetupDiGetDeviceInterfaceDetail(hDevInfo, ref deviceInterfaceData, detailDataPtr, requiredSize, out _, IntPtr.Zero)) { // 获取设备路径 string devicePath = Marshal.PtrToStringUni(detailDataPtr + (IntPtr.Size == 8 ? 8 : 4)); // 这里可以通过devicePath打开设备,调用DeviceIoControl获取USB设备的速度信息 // 比如获取USB_DEVICE_DESCRIPTOR,其中bMaxPacketSize0或者通过IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX获取速度 // 简化判断:如果设备路径对应的设备的最大速度是5Gbps或以上,就是USB3 // 这里省略设备打开和IOCTL调用的代码,你可以根据需求补充 // 假设我们通过设备ID匹配到目标设备后,判断速度 if (devicePath.Contains(targetDeviceId)) { // 这里替换成实际的速度判断逻辑 bool isUsb3 = CheckUsbDeviceSpeed(devicePath); Marshal.FreeHGlobal(detailDataPtr); return isUsb3; } } Marshal.FreeHGlobal(detailDataPtr); index++; } return false; } private static bool CheckUsbDeviceSpeed(string devicePath) { // 补充实现:通过DeviceIoControl获取USB设备的连接速度 // USB2的速度是480Mbps,USB3.0是5Gbps,USB3.1是10Gbps // 这里可以参考Windows SDK的USB相关IOCTL实现 // 示例逻辑(需完善): // 打开设备,发送IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX,获取DEVICE_USB_NODE_CONNECTION_INFORMATION_EX结构体 // 其中ConnectionSpeed字段对应速度,单位是kbps // return connectionSpeed >= 5000000; // 5Gbps = 5000000 kbps return false; } }
关键修复点
- 必须先调用一次
SetupDiGetDeviceInterfaceDetail获取requiredSize,再分配对应大小的缓冲区。 - 结构体
SP_DEVICE_INTERFACE_DETAIL_DATA的cbSize需要根据系统位数设置(32位设为4,64位设为8),这是很多人踩坑的地方。
最后给你几个调试建议
- 如果你用SetupDi方法,调用API后一定要用
Marshal.GetLastWin32Error()查看错误码,比如错误码122(ERROR_INSUFFICIENT_BUFFER)就是缓冲区不够,这时候就要按上面的方法先获取正确的大小。 - 不管用哪种方法,都要先在设备管理器里找到你的目标设备的硬件ID或PNP设备ID,这样过滤的时候才准确。
- 测试时可以分别插USB2和USB3端口,对比返回结果,确保逻辑正确。
备注:内容来源于stack exchange,提问作者GeorgeStorm
相关产品推荐
相关产品推荐

