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
相关产品推荐
相关产品推荐

