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

Windows下C#获取带自定义Bus reported device description的USB转串口设备COM端口

解决方案:匹配自定义Bus Reported Device Description获取对应FTDI设备的COM端口号

核心逻辑:底层USB设备的预设Bus reported description和上层虚拟COM口通过设备实例ID关联,需要分两次遍历Windows设备管理器的不同分类,再做关联匹配。

前置说明

你之前查询的结果只关联COM端口类设备的属性,拿到的是FTDI驱动替换后的描述,无法匹配预设值,所以需要先查「通用串行总线控制器」分类下的USB设备属性,拿到目标设备的实例ID前缀后,再匹配到对应的COM端口。

实现步骤

  • 调用Windows SetupAPI遍历通用串行总线控制器类(GUID:{36FC9E60-C465-11CF-8056-444553540000})下的所有设备,读取每个设备的Bus reported device description属性,匹配你预设的自定义值,记录匹配设备的实例ID前缀。
  • 再调用SetupAPI遍历端口(COM和LPT)类(GUID:{4D36E978-E325-11CE-BFC1-08002BE10318})下的所有COM端口设备,找到设备实例ID包含上述前缀的设备,提取对应的COM端口号。

完整C#实现代码

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

public class FtdiPortFinder
{
    // 设备类GUID定义
    private static readonly Guid UsbDeviceClassGuid = new Guid("36FC9E60-C465-11CF-8056-444553540000");
    private static readonly Guid ComPortClassGuid = new Guid("4D36E978-E325-11CE-BFC1-08002BE10318");
    
    // 设备属性键定义
    private const int SPDRP_BUSREPORTEDDEVICEDESC = 0x0000001F;
    private const int SPDRP_FRIENDLYNAME = 0x0000000C;
    private const int DIGCF_PRESENT = 0x00000002;

    [StructLayout(LayoutKind.Sequential)]
    private struct SP_DEVINFO_DATA
    {
        public int cbSize;
        public Guid ClassGuid;
        public int DevInst;
        public IntPtr Reserved;
    }

    [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
    private static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, IntPtr Enumerator, IntPtr hwndParent, int Flags);

    [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
    private static extern bool SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet, int MemberIndex, ref SP_DEVINFO_DATA DeviceInfoData);

    [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
    private static extern bool SetupDiGetDeviceRegistryProperty(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, int Property, out int PropertyRegDataType, StringBuilder PropertyBuffer, int PropertyBufferSize, out int RequiredSize);

    [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
    private static extern bool SetupDiGetDeviceInstanceId(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, StringBuilder DeviceInstanceId, int DeviceInstanceIdSize, out int RequiredSize);

    [DllImport("setupapi.dll", SetLastError = true)]
    private static extern bool SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);

    /// <summary>
    /// 根据自定义的Bus Reported Device Description查找对应的COM端口
    /// </summary>
    /// <param name="targetBusDesc">预设的自定义Bus描述</param>
    /// <returns>匹配的COM端口列表</returns>
    public static List<string> GetMatchingComPorts(string targetBusDesc)
    {
        List<string> resultPorts = new List<string>();
        List<string> targetInstanceIdPrefixes = new List<string>();

        // 第一步:查找USB类下匹配Bus描述的设备,记录实例ID前缀
        IntPtr usbDeviceSet = SetupDiGetClassDevs(ref UsbDeviceClassGuid, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT);
        if (usbDeviceSet == IntPtr.Zero) return resultPorts;

        try
        {
            SP_DEVINFO_DATA devInfo = new SP_DEVINFO_DATA();
            devInfo.cbSize = Marshal.SizeOf(devInfo);

            for (int i = 0; SetupDiEnumDeviceInfo(usbDeviceSet, i, ref devInfo); i++)
            {
                // 读取Bus reported device description
                StringBuilder descBuffer = new StringBuilder(256);
                if (SetupDiGetDeviceRegistryProperty(usbDeviceSet, ref devInfo, SPDRP_BUSREPORTEDDEVICEDESC, out _, descBuffer, descBuffer.Capacity, out _))
                {
                    if (descBuffer.ToString().Trim().Equals(targetBusDesc.Trim(), StringComparison.OrdinalIgnoreCase))
                    {
                        // 读取设备实例ID
                        StringBuilder idBuffer = new StringBuilder(256);
                        if (SetupDiGetDeviceInstanceId(usbDeviceSet, ref devInfo, idBuffer, idBuffer.Capacity, out _))
                        {
                            // 取实例ID前3段作为匹配前缀(适配FTDI设备的ID格式)
                            string[] idParts = idBuffer.ToString().Split('\\');
                            if (idParts.Length >= 3)
                            {
                                targetInstanceIdPrefixes.Add($"{idParts[0]}\\{idParts[1]}\\{idParts[2]}");
                            }
                        }
                    }
                }
            }
        }
        finally
        {
            SetupDiDestroyDeviceInfoList(usbDeviceSet);
        }

        if (targetInstanceIdPrefixes.Count == 0) return resultPorts;

        // 第二步:查找COM端口类下匹配实例ID前缀的设备,提取端口号
        IntPtr comDeviceSet = SetupDiGetClassDevs(ref ComPortClassGuid, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT);
        if (comDeviceSet == IntPtr.Zero) return resultPorts;

        try
        {
            SP_DEVINFO_DATA devInfo = new SP_DEVINFO_DATA();
            devInfo.cbSize = Marshal.SizeOf(devInfo);

            for (int i = 0; SetupDiEnumDeviceInfo(comDeviceSet, i, ref devInfo); i++)
            {
                StringBuilder idBuffer = new StringBuilder(256);
                if (SetupDiGetDeviceInstanceId(comDeviceSet, ref devInfo, idBuffer, idBuffer.Capacity, out _))
                {
                    string instanceId = idBuffer.ToString();
                    foreach (string prefix in targetInstanceIdPrefixes)
                    {
                        if (instanceId.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
                        {
                            // 从友好名称中提取COM端口号
                            StringBuilder nameBuffer = new StringBuilder(256);
                            if (SetupDiGetDeviceRegistryProperty(comDeviceSet, ref devInfo, SPDRP_FRIENDLYNAME, out _, nameBuffer, nameBuffer.Capacity, out _))
                            {
                                string friendlyName = nameBuffer.ToString();
                                int comIndex = friendlyName.IndexOf("(COM");
                                if (comIndex >= 0)
                                {
                                    int endIndex = friendlyName.IndexOf(")", comIndex);
                                    if (endIndex > comIndex)
                                    {
                                        string port = friendlyName.Substring(comIndex + 1, endIndex - comIndex - 1);
                                        resultPorts.Add(port);
                                    }
                                }
                            }
                            break;
                        }
                    }
                }
            }
        }
        finally
        {
            SetupDiDestroyDeviceInfoList(comDeviceSet);
        }

        return resultPorts;
    }

    // 调用示例
    public static void Main()
    {
        // 替换为你预设的Bus Reported Device Description
        string yourCustomDesc = "MY_CUSTOM_FTDI_DEVICE";
        List<string> ports = GetMatchingComPorts(yourCustomDesc);
        foreach (string port in ports)
        {
            Console.WriteLine("匹配到端口:" + port);
        }
    }
}

注意事项

  • 代码兼容.NET Framework 4.x及.NET Core/.NET 5+所有Windows平台版本,无需引入第三方NuGet包。
  • 如果存在多个相同自定义描述的FTDI设备,方法会返回所有匹配的COM端口列表。
  • 若需要精确匹配单台设备,可以在FTDI芯片中写入唯一序列号,调整实例ID前缀匹配逻辑到序列号层级即可。

内容的提问来源于stack exchange,提问作者esteenbrink

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 12:06:04