47

I have some code that loads the serial ports into a combo-box:

     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;

I would like to add the port descriptions (similar to what are shown for the COM ports in the Device Manager) to the list and sort the items in the list that are after index 0 (solved: see above snippet). Does anyone have any suggestions for adding the port descriptions? I am using Microsoft Visual C# 2008 Express Edition (.NET 2.0). Any thoughts you may have would be appreciated. Thanks.

Jim Fell
  • 13,750
  • 36
  • 127
  • 202

8 Answers8

48

I tried so many solutions on here that didn't work for me, only displaying some of the ports. But the following displayed All of them and their information.

        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);
            }
        }
    
habakuk
  • 2,712
  • 2
  • 28
  • 47
humudu
  • 699
  • 1
  • 7
  • 13
  • 1
    This is the only solution that worked for me also. The other solutions did not list the port I was looking for...this one did. – Joe Gayetty Jun 12 '18 at 18:07
  • 2
    you help me save hours – Huy - Logarit Sep 07 '18 at 16:13
  • 13
    This solution works. There is only a small problem in combining the portnames with the description if you have COM ports higher than 9. It connects e.g. COM16 with COM1 because it finds the string 'COM1' in 'COM16'. I fixed it by changing the s.Contains(n) with s.Contains('('+ n + ')') – Erik Jan 15 '20 at 08:53
  • 1
    This is the only working solution here. Other solution didn't list all ports or thew null pointer exceptions. – Jack Jul 14 '21 at 07:44
  • `SELECT * ...` can be replaced with `SELECT Caption ...`, seeing as that's the only property used later on. – Andreas Oct 10 '22 at 11:45
  • There's a way to do that also in linux? – Majico Nov 03 '22 at 15:30
43

EDIT: Sorry, I zipped past your question too quick. I realize now that you're looking for a list with the port name + port description. I've updated the code accordingly...

Using System.Management, you can query for all the ports, and all the information for each port (just like Device Manager...)

Sample code (make sure to add reference to 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();
        }
    }
}

More info here: http://msdn.microsoft.com/en-us/library/aa394582%28VS.85%29.aspx

code4life
  • 15,655
  • 7
  • 50
  • 82
  • What is the point of calling `SerialPort.GetPortNames();` and `join` when you can just use `p["DeviceID"]` from ManagementObject? – Kamil Jun 24 '19 at 12:36
  • Are you guaranteed fetching only serial ports? If not, how took you collect only serial ports? I'm curious to know... – code4life Jun 28 '19 at 22:46
  • 4
    It's not showing a usb to serial converted that's connected (it's showing up on device manager) – mrid Jan 23 '20 at 11:56
  • 6
    This answer is WRONG. Why does it have 44 up-votes??? It is wrong to search FROM WIN32_SerialPort. The other answers here do it correctly by searching FROM Win32_PnPEntity. Your code lists only 2 of my 6 COM ports !! All USB to RS232 adapter COM ports are completely missing! – Elmue Oct 26 '20 at 14:12
  • This didn't list all of my active COM ports whereas humudo's answer does. – Bill Tarbell Apr 06 '22 at 12:57
  • "WIN32_SerialPort" doesn't work - it doesn't list any ports at all! – Vincent Jul 25 '23 at 01:58
  • @Vincent: Keep in mind that this is a 13 year old answer, so the API may have changed in that time, between versions. – Jeremy Caney Jul 26 '23 at 00:09
34

Use following code snippet

It gives following output when executed.

serial port : Communications Port (COM1)
serial port : Communications Port (COM2)

Don't forget to add

using System;
using System.Management;
using System.Windows.Forms;

Also add reference to system.Management (by default it is not available)

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

Update: You may also check for

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

instead of

if (queryObj["Caption"].ToString().Contains("(COM"))
Sachin Chavan
  • 5,578
  • 5
  • 49
  • 75
  • 6
    I had to include a null check since my loop was evaluated 82 times with 3 matches. The rest returned null. if ((queryObj["Caption"] != null) && (queryObj["Caption"].ToString().Contains("(COM"))) – Larry May 16 '16 at 06:23
20

None of the answers here satisfies my needs.

The answer from Muno is wrong because it lists ONLY the USB ports.

The answer from code4life is wrong because it lists all EXCEPT the USB ports. (Nevertheless it has 44 up-votes!!!)

I have an EPSON printer simulation port on my computer which is not listed by any of the answers here. So I had to write my own solution. Additionally I want to display more information than just the caption string. I also need to separate the port name from the description.

My code has been tested on Windows XP, 7, 10 and 11.

The Port Name (like "COM1") must be read from the registry because WMI does not give this information for all COM ports (EPSON).

If you use my code you do not need SerialPort.GetPortNames() anymore. My function returns the same ports, but with additional details. Why did Microsoft not implement such a function into the framework??

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("-----------------------------------");
    }
}

I tested the code with a lot of COM ports. This is the Console output:

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 is a COM port on the mainboard.

COM 8 and 9 are Buetooth COM ports.

COM 25 and 26 are USB to RS232 adapters.

COM 28 and 29 and 30 are Arduino-like boards.

COM 20 and 999 are EPSON ports.

Elmue
  • 7,602
  • 3
  • 47
  • 57
  • 1
    This is lovely, thank you so much! Also thanks for updating the post after doing more tests. I used your method with little modifications to identify which COM port a specific HW is connected to via its VID and PID. Works like a charm on Windows 10. – Janis Sep 29 '22 at 12:44
17

There is a post about this same issue on MSDN:

Getting more information about a serial port in C#

Hi Ravenb,

We can't get the information through the SerialPort type. I don't know why you need this info in your application. However, there's a solved thread with the same question as you. You can check out the code there, and see if it can help you.

If you have any further problem, please feel free to let me know.

Best regards, Bruce Zhou

The link in that post goes to this one:

How to get more info about port using System.IO.Ports.SerialPort

You can probably get this info from a WMI query. Check out this tool to help you find the right code. Why would you care though? This is just a detail for a USB emulator, normal serial ports won't have this. A serial port is simply know by "COMx", nothing more.

Bradley Mountford
  • 8,195
  • 4
  • 41
  • 41
  • The link to the solved thread had the code I was after. A bit old but it works to get the COM port number from PID&VID. Thanks +1 – Piotr Kula Jan 28 '14 at 10:22
  • 37
    Some painfully inexperienced responses in those quotes. There are lots of obvious reasons to want this info. If you're having a user pick a port to use you want to show the device name (it's not 1995, so it's not OK to just give a list of COM ports), and if you're working with a specific known device, you can use device info to automatically pick the correct port. – Glenn Maynard Nov 06 '15 at 16:21
12

I combined previous answers and used structure of Win32_PnPEntity class which can be found found here. Got solution like this:

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 class:

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;       

}
Muno
  • 749
  • 2
  • 9
  • 19
  • Probably the best solution on this page. Apart from being able to filter by other fields, you also get all the other device information you might need. – Mark Feldman May 14 '20 at 01:17
  • If there are many com port devices connected how do we know who is who? I see many devices doesnt have hardwareid at all!!! So there is no way to identify it automatically and we always have to select COM port manualy!!!! ((( – NoWar Jul 30 '20 at 08:00
  • This is definitely the best answer on the page, only addition I made was to add a COMPort property and extract the actual com port value (e.g. COM15) from the caption just to make it easier for use. But an excellence solution. – Nathan Sep 12 '20 at 07:25
  • This answer is WRONG. Why do you list only the USB ports?? The COM port on my mainboard is omitted by your code. The question does not ask specifically for USB COM ports. Remove that: && name.ToString().Contains("USB") – Elmue Oct 26 '20 at 14:25
2

I'm not quite sure what you mean by "sorting the items after index 0", but if you just want to sort the array of strings returned by SerialPort.GetPortNames(), you can use Array.Sort.

Heinzi
  • 167,459
  • 57
  • 363
  • 519
-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()
);
René Höhle
  • 26,716
  • 22
  • 73
  • 82
Ken
  • 11
  • 1
  • 9
    Rather than only post a block of code, please *explain* why this code solves the problem posed. Without an explanation, this is not an answer. – Martijn Pieters Nov 29 '12 at 10:13