You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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需要先调用一次获取所需的缓冲区大小,再分配足够空间后第二次调用才能成功。下面是正确的实现步骤:

核心思路

  1. 初始化设备接口列表,指定USB设备的GUID。
  2. 枚举所有USB设备接口。
  3. 第一次调用SetupDiGetDeviceInterfaceDetail获取所需的缓冲区大小。
  4. 分配对应大小的缓冲区,第二次调用获取设备详细信息。
  5. 通过设备路径调用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),这是很多人踩坑的地方。

最后给你几个调试建议

  1. 如果你用SetupDi方法,调用API后一定要用Marshal.GetLastWin32Error()查看错误码,比如错误码122(ERROR_INSUFFICIENT_BUFFER)就是缓冲区不够,这时候就要按上面的方法先获取正确的大小。
  2. 不管用哪种方法,都要先在设备管理器里找到你的目标设备的硬件ID或PNP设备ID,这样过滤的时候才准确。
  3. 测试时可以分别插USB2和USB3端口,对比返回结果,确保逻辑正确。

备注:内容来源于stack exchange,提问作者GeorgeStorm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.21 11:24:35