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

如何在C++/C#中获取COM端口列表及对应设备序列号

在C#中列出带设备ID(序列号)的活跃COM端口

针对你提到的多PC间串口设备流转、需要自动识别COM端口的需求,以下两种方案可实现获取COM端口对应设备ID/序列号的功能,均支持Windows 7及以上系统,适配FTDI、Silicon Labs等USB转RS232转换器。

方法一:使用WMI(快速实现)

通过Windows Management Instrumentation查询系统硬件信息,关联COM端口与设备属性,代码实现简单,适合大多数场景。需先添加System.Management引用。

代码示例

using System;
using System.Management;
using System.IO.Ports;

class Program
{
    static void Main(string[] args)
    {
        string[] portNames = SerialPort.GetPortNames();

        foreach (string portName in portNames)
        {
            // 查询包含当前COM端口的设备实体
            var query = new ObjectQuery($"SELECT * FROM Win32_PnPEntity WHERE Name LIKE '%{portName}%'");
            using (var searcher = new ManagementObjectSearcher(query))
            {
                foreach (ManagementObject obj in searcher.Get())
                {
                    string deviceId = obj["DeviceID"]?.ToString();
                    string serialNumber = obj["SerialNumber"]?.ToString();
                    string friendlyName = obj["Name"]?.ToString();

                    Console.WriteLine($"COM端口: {portName}");
                    Console.WriteLine($"设备名称: {friendlyName}");
                    Console.WriteLine($"设备ID: {deviceId}");
                    Console.WriteLine($"序列号: {serialNumber ?? "未找到"}");
                    Console.WriteLine("---");
                }
            }
        }

        Console.ReadLine();
    }
}

适配说明

  • FTDI设备:序列号通常包含在DeviceID中(格式示例:FTDIBUS\VID_0403+PID_6001+A123456A\0000,其中A123456A即为序列号)
  • Silicon Labs设备:可直接读取SerialNumber属性,或从DeviceID中提取对应字段

方法二:使用SetupAPI(底层可靠)

若WMI存在属性缺失或延迟问题,可直接调用Windows底层SetupAPI函数,兼容性更强,适合对设备信息精度要求高的场景。

代码示例

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

public class ComPortInfo
{
    public string PortName { get; set; }
    public string DeviceId { get; set; }
    public string SerialNumber { get; set; }
}

class Program
{
    // P/Invoke 声明SetupAPI函数
    [DllImport("setupapi.dll", SetLastError = true)]
    private static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, IntPtr Enumerator, IntPtr hwndParent, uint Flags);

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

    [DllImport("setupapi.dll", SetLastError = true)]
    private static extern bool SetupDiGetDeviceRegistryProperty(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, uint Property, out uint PropertyRegDataType, StringBuilder 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;
    }

    // SetupAPI常量
    private const uint DIGCF_PRESENT = 0x00000002;
    private const uint DIGCF_DEVICEINTERFACE = 0x00000010;
    private const uint SPDRP_HARDWAREID = 0x00000001;
    private const uint SPDRP_FRIENDLYNAME = 0x0000000C;
    private const uint SPDRP_SERIALNUMBER = 0x00000014;

    // 串口设备类GUID
    private static readonly Guid GUID_DEVINTERFACE_COMPORT = new Guid("{86e0d1e0-8089-11d0-9ce4-08003e301f73}");

    static void Main(string[] args)
    {
        List<ComPortInfo> comPorts = GetComPortsWithDeviceInfo();

        foreach (ComPortInfo port in comPorts)
        {
            Console.WriteLine($"COM端口: {port.PortName}");
            Console.WriteLine($"设备ID: {port.DeviceId}");
            Console.WriteLine($"序列号: {port.SerialNumber ?? "未找到"}");
            Console.WriteLine("---");
        }

        Console.ReadLine();
    }

    private static List<ComPortInfo> GetComPortsWithDeviceInfo()
    {
        List<ComPortInfo> comPorts = new List<ComPortInfo>();
        IntPtr deviceInfoSet = IntPtr.Zero;

        try
        {
            // 获取当前存在的所有串口设备
            deviceInfoSet = SetupDiGetClassDevs(ref GUID_DEVINTERFACE_COMPORT, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
            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++)
            {
                ComPortInfo portInfo = new ComPortInfo();

                // 提取COM端口号
                StringBuilder friendlyName = new StringBuilder(256);
                if (SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref deviceInfoData, SPDRP_FRIENDLYNAME, out _, friendlyName, (uint)friendlyName.Capacity, out _))
                {
                    string name = friendlyName.ToString();
                    int comIndex = name.IndexOf("(COM");
                    if (comIndex != -1)
                    {
                        portInfo.PortName = name.Substring(comIndex + 1, name.Length - comIndex - 2);
                    }
                }

                // 获取硬件ID(设备ID)
                StringBuilder hardwareId = new StringBuilder(1024);
                if (SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref deviceInfoData, SPDRP_HARDWAREID, out _, hardwareId, (uint)hardwareId.Capacity, out _))
                {
                    portInfo.DeviceId = hardwareId.ToString();
                    // 提取FTDI序列号
                    if (portInfo.DeviceId.Contains("FTDIBUS"))
                    {
                        string[] parts = portInfo.DeviceId.Split('+');
                        if (parts.Length >= 3)
                        {
                            portInfo.SerialNumber = parts[2].Split('\\')[0];
                        }
                    }
                }

                // 获取序列号(适配Silicon Labs等设备)
                StringBuilder serialNumber = new StringBuilder(256);
                if (SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref deviceInfoData, SPDRP_SERIALNUMBER, out _, serialNumber, (uint)serialNumber.Capacity, out _))
                {
                    portInfo.SerialNumber = serialNumber.ToString();
                }

                if (!string.IsNullOrEmpty(portInfo.PortName))
                    comPorts.Add(portInfo);
            }
        }
        finally
        {
            if (deviceInfoSet != IntPtr.Zero)
                SetupDiDestroyDeviceInfoList(deviceInfoSet);
        }

        return comPorts;
    }
}

适配说明

  • FTDI设备:从硬件ID的+分隔段中提取序列号
  • Silicon Labs设备:优先读取SPDRP_SERIALNUMBER属性,该属性会直接返回手动设置的设备SN
  • 两种设备手动设置的SN都会直接反映在对应字段中,无需额外处理

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 13:35:18