获取串口信息

47

我有一些代码,可以将串口加载到组合框中:

     List<String> tList = new List<String>(); 

     comboBoxComPort.Items.Clear();

     foreach (string s in SerialPort.GetPortNames())
     {
        tList.Add(s);
     }

     tList.Sort();
     comboBoxComPort.Items.Add("Select COM port...");
     comboBoxComPort.Items.AddRange(tList.ToArray());
     comboBoxComPort.SelectedIndex = 0;

我想在列表中添加端口描述(类似于设备管理器中显示的COM端口),有没有人有关于如何添加端口描述的建议呢?我正在使用Microsoft Visual C# 2008 Express Edition (.NET 2.0)。感谢任何您可能拥有的想法。


请查看以下链接: https://dev59.com/BknSa4cB1Zd3GeqPLzW4 - volody
把SerialPort.GetPortNames()的结果存储在一个变量中,进行排序,然后循环遍历,这个想法怎么样? - Maciej Hehl
9个回答

50

我在这里尝试了很多解决方案,但它们对我没有用处,只显示了其中的一些端口。但是以下方法显示了所有端口及其信息。

        using (var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_PnPEntity WHERE Caption like '%(COM%'"))
        {
            var portnames = SerialPort.GetPortNames();
            var ports = searcher.Get().Cast<ManagementBaseObject>().ToList().Select(p => p["Caption"].ToString());

            var portList = portnames.Select(n => n + " - " + ports.FirstOrDefault(s => s.Contains(n))).ToList();
            
            foreach(string s in portList)
            {
                Console.WriteLine(s);
            }
        }
    

1
这是唯一一个对我有效的解决方案。其他的解决方案没有列出我正在寻找的端口...而这个解决方案做到了。 - Joe Gayetty
2
你帮我节省了好几个小时。 - Huy - Logarit
14
这个解决方案可行。如果您的COM端口号大于9,则在将端口名称与描述组合时仅存在一个小问题。它会将例如COM16与COM1连接,因为它在“COM16”中找到了字符串“COM1”。我通过将s.Contains(n)更改为s.Contains('('+ n + ')')来修复了它。 - Erik
1
这是唯一有效的解决方案。其他解决方案没有列出所有端口或抛出空指针异常。 - Jack
由于后面只使用了“Caption”属性,因此可以将“SELECT * ...”替换为“SELECT Caption ...”。 - Andreas
在Linux中也有一种方法可以做到这一点吗? - Majico

43

编辑: 对不起,我太匆忙地忽略了您的问题。 我现在意识到你在寻找一个带有端口名称和端口描述的列表。 我已经相应地更新了代码...

使用System.Management,您可以查询所有端口及每个端口的所有信息(就像设备管理器一样...)

示例代码(确保添加对System.Management的引用):

using System;
using System.Management;
using System.Collections.Generic;
using System.Linq;
using System.IO.Ports;        

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var searcher = new ManagementObjectSearcher
                ("SELECT * FROM WIN32_SerialPort"))
            {
                string[] portnames = SerialPort.GetPortNames();
                var ports = searcher.Get().Cast<ManagementBaseObject>().ToList();
                var tList = (from n in portnames
                            join p in ports on n equals p["DeviceID"].ToString()
                            select n + " - " + p["Caption"]).ToList();

                tList.ForEach(Console.WriteLine);
            }

            // pause program execution to review results...
            Console.WriteLine("Press enter to exit");
            Console.ReadLine();
        }
    }
}

更多信息请参见:http://msdn.microsoft.com/en-us/library/aa394582%28VS.85%29.aspx


当你可以直接使用ManagementObject中的p["DeviceID"]时,为什么要调用SerialPort.GetPortNames();join的作用是什么? - Kamil
你能保证只获取串口吗?如果不能,你是如何收集只有串口的呢?我很好奇... - code4life
4
它没有显示连接的USB串行转换器(在设备管理器中显示)。 - mrid
6
这个答案是错误的,为什么会有44个赞?在WIN32_SerialPort中搜索是错误的。这里的其他答案通过从Win32_PnPEntity进行搜索来正确执行此操作。您的代码仅列出了我的6个COM端口中的2个!! 所有USB到RS232适配器的COM端口都完全丢失了! - Elmue
这个没有列出我所有的活动 COM 端口,而 humudo 的答案则做到了。 - Bill Tarbell
显示剩余2条评论

34
使用以下代码片段:
执行时会产生以下输出。
serial port : Communications Port (COM1)
serial port : Communications Port (COM2)

不要忘记添加。
using System;
using System.Management;
using System.Windows.Forms;

还需添加对 system.Management 的引用(默认情况下不可用)。 C#
private void GetSerialPort()
{

    try
    {
        ManagementObjectSearcher searcher = 
            new ManagementObjectSearcher("root\\CIMV2", 
            "SELECT * FROM Win32_PnPEntity"); 

        foreach (ManagementObject queryObj in searcher.Get())
        {
            if (queryObj["Caption"].ToString().Contains("(COM"))
            {
                Console.WriteLine("serial port : {0}", queryObj["Caption"]);
            }

        }
    }
    catch (ManagementException e)
    {
        MessageBox.Show( e.Message);
    }

}

VB

  Private Sub GetAllSerialPortsName()
        Try
            Dim searcher As New ManagementObjectSearcher("root\CIMV2", "SELECT * FROM Win32_PnPEntity")
            For Each queryObj As ManagementObject In searcher.Get()
                If InStr(queryObj("Caption"), "(COM") > 0 Then
                    Console.WriteLine("serial port : {0}", queryObj("Caption"))
                End If
            Next
        Catch err As ManagementException
            MsgBox(err.Message)
        End Try
    End Sub

更新: 你也可以检查

if (queryObj["Caption"].ToString().StartsWith("serial port"))

替代

if (queryObj["Caption"].ToString().Contains("(COM"))

6
由于我的循环被执行了82次,但只有3个匹配项,所以我不得不添加一个空值检查。其他部分返回空值。 if ((queryObj["Caption"] != null) && (queryObj["Caption"].ToString().Contains("(COM"))) - Larry

21

这里没有任何答案能够满足我的需求。

Muno的答案错误,因为它只列出了USB端口。

code4life的答案错误,因为它列出了除USB端口外的所有端口。(尽管它有44个赞!!!)

我的电脑上有一个EPSON打印机模拟端口,在这些答案中没有被列出。所以我不得不写自己的解决方案。此外,我想显示比仅仅标题字符串更多的信息。我还需要将端口名称与描述分开。

我的代码已在Windows XP、7、10和11上进行过测试。

端口名称(如"COM1")必须从注册表中读取,因为WMI并不为所有COM端口(EPSON)提供此信息。

如果您使用我的代码,就不再需要SerialPort.GetPortNames()。我的函数返回相同的端口,但带有附加详细信息。为什么Microsoft没有把这样的功能实现到框架中呢?

using System.Management;
using Microsoft.Win32;

using (ManagementClass i_Entity = new ManagementClass("Win32_PnPEntity"))
{
    foreach (ManagementObject i_Inst in i_Entity.GetInstances())
    {
        Object o_Guid = i_Inst.GetPropertyValue("ClassGuid");
        if (o_Guid == null || o_Guid.ToString().ToUpper() != "{4D36E978-E325-11CE-BFC1-08002BE10318}")
            continue; // Skip all devices except device class "PORTS"

        String s_Caption  = i_Inst.GetPropertyValue("Caption")     .ToString();
        String s_Manufact = i_Inst.GetPropertyValue("Manufacturer").ToString();
        String s_DeviceID = i_Inst.GetPropertyValue("PnpDeviceID") .ToString();
        String s_RegPath  = "HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Enum\\" + s_DeviceID + "\\Device Parameters";
        String s_PortName = Registry.GetValue(s_RegPath, "PortName", "").ToString();

        int s32_Pos = s_Caption.IndexOf(" (COM");
        if (s32_Pos > 0) // remove COM port from description
            s_Caption = s_Caption.Substring(0, s32_Pos);

        Console.WriteLine("Port Name:    " + s_PortName);
        Console.WriteLine("Description:  " + s_Caption);
        Console.WriteLine("Manufacturer: " + s_Manufact);
        Console.WriteLine("Device ID:    " + s_DeviceID);
        Console.WriteLine("-----------------------------------");
    }
}

我使用了很多COM端口来测试代码。以下是控制台的输出:

Port Name:    COM29
Description:  CDC Interface (Virtual COM Port) for USB Debug
Manufacturer: GHI Electronics, LLC
Device ID:    USB\VID_1B9F&PID_F003&MI_01\6&3009671A&0&0001
-----------------------------------
Port Name:    COM28
Description:  Teensy USB Serial
Manufacturer: PJRC.COM, LLC.
Device ID:    USB\VID_16C0&PID_0483\1256310
-----------------------------------
Port Name:    COM25
Description:  USB-SERIAL CH340
Manufacturer: wch.cn
Device ID:    USB\VID_1A86&PID_7523\5&2499667D&0&3
-----------------------------------
Port Name:    COM26
Description:  Prolific USB-to-Serial Comm Port
Manufacturer: Prolific
Device ID:    USB\VID_067B&PID_2303\5&2499667D&0&4
-----------------------------------
Port Name:    COM1
Description:  Comunications Port
Manufacturer: (Standard port types)
Device ID:    ACPI\PNP0501\1
-----------------------------------
Port Name:    COM999
Description:  EPSON TM Virtual Port Driver
Manufacturer: EPSON
Device ID:    ROOT\PORTS\0000
-----------------------------------
Port Name:    COM20
Description:  EPSON COM Emulation USB Port
Manufacturer: EPSON
Device ID:    ROOT\PORTS\0001
-----------------------------------
Port Name:    COM8
Description:  Standard Serial over Bluetooth link
Manufacturer: Microsoft
Device ID:    BTHENUM\{00001101-0000-1000-8000-00805F9B34FB}_LOCALMFG&000F\8&3ADBDF90&0&001DA568988B_C00000000
-----------------------------------
Port Name:    COM9
Description:  Standard Serial over Bluetooth link
Manufacturer: Microsoft
Device ID:    BTHENUM\{00001101-0000-1000-8000-00805F9B34FB}_LOCALMFG&0000\8&3ADBDF90&0&000000000000_00000002
-----------------------------------
Port Name:    COM30
Description:  Arduino Uno
Manufacturer: Arduino LLC (www.arduino.cc)
Device ID:    USB\VID_2341&PID_0001\74132343530351F03132
-----------------------------------

COM1 是主板上的一个串口。

COM 8 和 9 是蓝牙串口。

COM 25 和 26 是 USB 转 RS232 适配器。

COM 28、29 和 30 是类似 Arduino 的开发板。

COM 20 和 999 是 EPSON 端口。


1
这太棒了,非常感谢!还要感谢您在进行更多测试后更新帖子。我使用了您的方法,并进行了一些小修改,以通过其VID和PID识别特定硬件连接到哪个COM端口。在Windows 10上运行得非常好。 - Janis

17

这个问题在MSDN上有一篇相关的帖子

如何使用C#获取串口的更多信息

你好,Ravenb,

我们无法通过SerialPort类型获取信息。我不知道你的应用程序为什么需要这些信息。然而,有一个已解决的线程与你的问题相同。你可以查看那里的代码,看看它是否能帮助你。

如果你有任何进一步的问题,请随时让我知道。

最好的问候, Bruce Zhou

该帖子中的链接指向这个页面:

如何使用System.IO.Ports.SerialPort获取有关端口的更多信息

您可能可以通过WMI查询获取此信息。请查看此工具以帮助您找到正确的代码。但是,您为什么要关心这个呢?这只是USB仿真器的细节,普通串行端口不会有这个。串行端口仅由“COMx”表示,没有其他信息。


已解决的线程链接中包含了我需要的代码。虽然有点老,但可以用来获取PID和VID对应的COM端口号。谢谢 +1 - Piotr Kula
38
这些引用中有一些非常缺乏经验的回复。想要这个信息有很多显而易见的原因。如果让用户选择一个端口使用,你需要显示设备名称(现在已经不是1995年了,所以仅给出COM端口列表是不够的),如果你正在使用一个特定的已知设备,你可以使用设备信息来自动选择正确的端口。 - Glenn Maynard

12

我结合了之前的答案,并使用了Win32_PnPEntity类的结构,该结构可以在此处找到:这里。得到的解决方案如下:

using System.Management;
public static void Main()
{
     GetPortInformation();
}

public string GetPortInformation()
    {
        ManagementClass processClass = new ManagementClass("Win32_PnPEntity");
        ManagementObjectCollection Ports = processClass.GetInstances();           
        foreach (ManagementObject property in Ports)
        {
            var name = property.GetPropertyValue("Name");               
            if (name != null && name.ToString().Contains("USB") && name.ToString().Contains("COM"))
            {
                var portInfo = new SerialPortInfo(property);
                //Thats all information i got from port.
                //Do whatever you want with this information
            }
        }
        return string.Empty;
    }

SerialPortInfo类:

public class SerialPortInfo
{
    public SerialPortInfo(ManagementObject property)
    {
        this.Availability = property.GetPropertyValue("Availability") as int? ?? 0;
        this.Caption = property.GetPropertyValue("Caption") as string ?? string.Empty;
        this.ClassGuid = property.GetPropertyValue("ClassGuid") as string ?? string.Empty;
        this.CompatibleID = property.GetPropertyValue("CompatibleID") as string[] ?? new string[] {};
        this.ConfigManagerErrorCode = property.GetPropertyValue("ConfigManagerErrorCode") as int? ?? 0;
        this.ConfigManagerUserConfig = property.GetPropertyValue("ConfigManagerUserConfig") as bool? ?? false;
        this.CreationClassName = property.GetPropertyValue("CreationClassName") as string ?? string.Empty;
        this.Description = property.GetPropertyValue("Description") as string ?? string.Empty;
        this.DeviceID = property.GetPropertyValue("DeviceID") as string ?? string.Empty;
        this.ErrorCleared = property.GetPropertyValue("ErrorCleared") as bool? ?? false;
        this.ErrorDescription = property.GetPropertyValue("ErrorDescription") as string ?? string.Empty;
        this.HardwareID = property.GetPropertyValue("HardwareID") as string[] ?? new string[] { };
        this.InstallDate = property.GetPropertyValue("InstallDate") as DateTime? ?? DateTime.MinValue;
        this.LastErrorCode = property.GetPropertyValue("LastErrorCode") as int? ?? 0;
        this.Manufacturer = property.GetPropertyValue("Manufacturer") as string ?? string.Empty;
        this.Name = property.GetPropertyValue("Name") as string ?? string.Empty;
        this.PNPClass = property.GetPropertyValue("PNPClass") as string ?? string.Empty;
        this.PNPDeviceID = property.GetPropertyValue("PNPDeviceID") as string ?? string.Empty;
        this.PowerManagementCapabilities = property.GetPropertyValue("PowerManagementCapabilities") as int[] ?? new int[] { };
        this.PowerManagementSupported = property.GetPropertyValue("PowerManagementSupported") as bool? ?? false;
        this.Present = property.GetPropertyValue("Present") as bool? ?? false;
        this.Service = property.GetPropertyValue("Service") as string ?? string.Empty;
        this.Status = property.GetPropertyValue("Status") as string ?? string.Empty;
        this.StatusInfo = property.GetPropertyValue("StatusInfo") as int? ?? 0;
        this.SystemCreationClassName = property.GetPropertyValue("SystemCreationClassName") as string ?? string.Empty;
        this.SystemName = property.GetPropertyValue("SystemName") as string ?? string.Empty;
    }

    int Availability;
    string Caption;
    string ClassGuid;
    string[] CompatibleID;
    int ConfigManagerErrorCode;
    bool ConfigManagerUserConfig;
    string CreationClassName;
    string Description;
    string DeviceID;
    bool ErrorCleared;
    string ErrorDescription;
    string[] HardwareID;
    DateTime InstallDate;
    int LastErrorCode;
    string Manufacturer;
    string Name;
    string PNPClass;
    string PNPDeviceID;
    int[] PowerManagementCapabilities;
    bool PowerManagementSupported;
    bool Present;
    string Service;
    string Status;
    int StatusInfo;
    string SystemCreationClassName;
    string SystemName;       

}

这可能是此页面上最好的解决方案。除了能够按其他字段进行过滤外,您还可以获得所有其他可能需要的设备信息。 - Mark Feldman
如果连接了许多COM端口设备,我们如何知道谁是谁?我发现许多设备根本没有硬件ID!因此,无法自动识别它,我们总是必须手动选择COM端口!!!((( - NoWar
这绝对是页面上最好的答案,我所做的唯一补充是添加了一个COM端口属性,并从标题中提取实际的COM端口值(例如COM15),以便于使用。但这是一种极佳的解决方案。 - Nathan
这个答案是错误的。为什么你只列出USB端口?我的主板上的COM端口被你的代码省略了。问题并没有特别要求USB COM端口。请移除:&& name.ToString().Contains("USB") - Elmue

2
我不确定你所说的“在索引0之后排序项目”的确切含义,但如果你只想对SerialPort.GetPortNames()返回的字符串数组进行排序,可以使用Array.Sort。请注意保留HTML标签。

我已经在我的原始问题中添加了细节。谢谢。 - Jim Fell

0
我重新编写了Elmue的答案,并使用Muno的SerialPortInfo类进行了丰富。因此,它更清晰并提供了更多信息。(.net 6)
using Microsoft.Win32;
using System.Management;
using System.Runtime.Versioning;

public static class SerialPortSearcher
{

    [SupportedOSPlatform("windows")]
    public static IEnumerable<ISerialPortInfo> Search()
    {
        using var entity = new ManagementClass("Win32_PnPEntity");
        foreach (var instance in entity.GetInstances().Cast<ManagementObject>())
        {
            var classGuid = instance.GetPropertyValue("ClassGuid");
            // Skip all devices except device class "PORTS"
            if (classGuid?.ToString()?.ToUpper() == "{4D36E978-E325-11CE-BFC1-08002BE10318}")
            {
                yield return new SerialPortInfo(instance);
            }
        }
    }

    [SupportedOSPlatform("windows")]
    private class SerialPortInfo : ISerialPortInfo
    {
        public SerialPortInfo(ManagementObject obj)
        {
            this.Availability = obj.GetPropertyValue("Availability") as int? ?? 0;
            this.Caption = obj.GetPropertyValue("Caption") as string ?? string.Empty;
            this.ClassGuid = obj.GetPropertyValue("ClassGuid") as string ?? string.Empty;
            this.CompatibleID = obj.GetPropertyValue("CompatibleID") as string[] ?? new string[] { };
            this.ConfigManagerErrorCode = obj.GetPropertyValue("ConfigManagerErrorCode") as int? ?? 0;
            this.ConfigManagerUserConfig = obj.GetPropertyValue("ConfigManagerUserConfig") as bool? ?? false;
            this.CreationClassName = obj.GetPropertyValue("CreationClassName") as string ?? string.Empty;
            this.Description = obj.GetPropertyValue("Description") as string ?? string.Empty;
            this.DeviceID = obj.GetPropertyValue("DeviceID") as string ?? string.Empty;
            this.ErrorCleared = obj.GetPropertyValue("ErrorCleared") as bool? ?? false;
            this.ErrorDescription = obj.GetPropertyValue("ErrorDescription") as string ?? string.Empty;
            this.HardwareID = obj.GetPropertyValue("HardwareID") as string[] ?? new string[] { };
            this.InstallDate = obj.GetPropertyValue("InstallDate") as DateTime? ?? DateTime.MinValue;
            this.LastErrorCode = obj.GetPropertyValue("LastErrorCode") as int? ?? 0;
            this.Manufacturer = obj.GetPropertyValue("Manufacturer") as string ?? string.Empty;
            this.Name = obj.GetPropertyValue("Name") as string ?? string.Empty;
            this.PNPClass = obj.GetPropertyValue("PNPClass") as string ?? string.Empty;
            this.PNPDeviceID = obj.GetPropertyValue("PnpDeviceID") as string ?? string.Empty;
            this.PowerManagementCapabilities = obj.GetPropertyValue("PowerManagementCapabilities") as int[] ?? new int[] { };
            this.PowerManagementSupported = obj.GetPropertyValue("PowerManagementSupported") as bool? ?? false;
            this.Present = obj.GetPropertyValue("Present") as bool? ?? false;
            this.Service = obj.GetPropertyValue("Service") as string ?? string.Empty;
            this.Status = obj.GetPropertyValue("Status") as string ?? string.Empty;
            this.StatusInfo = obj.GetPropertyValue("StatusInfo") as int? ?? 0;
            this.SystemCreationClassName = obj.GetPropertyValue("SystemCreationClassName") as string ?? string.Empty;
            this.SystemName = obj.GetPropertyValue("SystemName") as string ?? string.Empty;

            var regPath = "HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Enum\\" + PNPDeviceID + "\\Device Parameters";
            this.PortName = Registry.GetValue(regPath, "PortName", "")?.ToString();

            //int i = Caption.IndexOf(" (COM");
            //if (i > 0) // remove COM port from description
            //    Caption = Caption.Substring(0, i);

        }

        public int Availability { get; }
        public string Caption { get; }
        public string ClassGuid { get; }
        public string[] CompatibleID { get; }
        public int ConfigManagerErrorCode { get; }
        public bool ConfigManagerUserConfig { get; }
        public string CreationClassName { get; }
        public string Description { get; }
        public string DeviceID { get; }
        public bool ErrorCleared { get; }
        public string ErrorDescription { get; }
        public string[] HardwareID { get; }
        public DateTime InstallDate { get; }
        public int LastErrorCode { get; }
        public string Manufacturer { get; }
        public string Name { get; }
        public string PNPClass { get; }
        public string PNPDeviceID { get; }
        public int[] PowerManagementCapabilities { get; }
        public bool PowerManagementSupported { get; }
        public bool Present { get; }
        public string Service { get; }
        public string Status { get; }
        public int StatusInfo { get; }
        public string SystemCreationClassName { get; }
        public string SystemName { get; }
        public string? PortName { get; }
    }
}

public interface ISerialPortInfo
{
    int Availability { get; }
    string Caption { get; }
    string ClassGuid { get; }
    string[] CompatibleID { get; }
    int ConfigManagerErrorCode { get; }
    bool ConfigManagerUserConfig { get; }
    string CreationClassName { get; }
    string Description { get; }
    string DeviceID { get; }
    bool ErrorCleared { get; }
    string ErrorDescription { get; }
    string[] HardwareID { get; }
    DateTime InstallDate { get; }
    int LastErrorCode { get; }
    string Manufacturer { get; }
    string Name { get; }
    string PNPClass { get; }
    string PNPDeviceID { get; }
    string? PortName { get; }
    int[] PowerManagementCapabilities { get; }
    bool PowerManagementSupported { get; }
    bool Present { get; }
    string Service { get; }
    string Status { get; }
    int StatusInfo { get; }
    string SystemCreationClassName { get; }
    string SystemName { get; }
}


Program.cs中进行测试:
using System.Text.Json;

var ports = SerialPortSearcher.Search().ToArray();

Console.WriteLine(JsonSerializer.Serialize(ports, new JsonSerializerOptions { WriteIndented = true }));


-3
this.comboPortName.Items.AddRange(
    (from qP in System.IO.Ports.SerialPort.GetPortNames()
     orderby System.Text.RegularExpressions.Regex.Replace(qP, "~\\d",
     string.Empty).PadLeft(6, '0')
     select qP).ToArray()
);

9
不要只贴一大段代码,请解释为什么这段代码可以解决所提出的问题。没有解释,这不是一个答案。 - Martijn Pieters

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接