How to scan for COM ports in C#?
System.IO.Ports is the namespace you want.
SerialPort.GetPortNames will list all serial COM ports.
Unfortunately, parallel ports are not supported directly from C#, as they're very infrequently used except in legacy situations. That said, you can list them by querying the following registry key:
HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\PARALLEL PORTS
See the Microsoft.Win32 namespace for details.
Use WMI through the System.Management namespace. A quick Google finds this code:
using System;
using System.Management;
public class Foo
{
public static void Main()
{
var instances = new ManagementClass("Win32_SerialPort").GetInstances();
foreach ( ManagementObject port in instances )
{
Console.WriteLine("{0}: {1}", port["deviceid"], port["name"]);
}
}
After few approaches I wrote something like this:
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Management;
using System.Text.RegularExpressions;
namespace Julo.SerialComm
{
public static class SerialPorts
{
public static List<SerialPortInfo> GetSerialPortsInfo()
{
var retList = new List<SerialPortInfo>();
// System.IO.Ports.SerialPort.GetPortNames() returns port names from Windows Registry
var registryPortNames = SerialPort.GetPortNames().ToList();
var managementObjectSearcher = new ManagementObjectSearcher(@"SELECT * FROM Win32_PnPEntity");
var managementObjectCollection = managementObjectSearcher.Get();
foreach (var n in registryPortNames)
{
Console.WriteLine($"Searching for {n}");
foreach (var p in managementObjectCollection)
{
if (p["Caption"] != null)
{
string caption = p["Caption"].ToString();
string pnpdevid = p["PnPDeviceId"].ToString();
if (caption.Contains("(" + n + ")"))
{
Console.WriteLine("PnPEntity port found: " + caption);
var props = p.Properties.Cast<PropertyData>().ToArray();
retList.Add(new SerialPortInfo(n, caption, pnpdevid));
break;
}
}
}
}
retList.Sort();
return retList;
}
public class SerialPortInfo : IComparable
{
public SerialPortInfo() {}
public SerialPortInfo(string name, string caption, string pnpdeviceid)
{
Name = name;
Caption = caption;
PNPDeviceID = pnpdeviceid;
// build shorter version of PNPDeviceID for better reading
// from this: BTHENUM\{00001101-0000-1000-8000-00805F9B34FB}_LOCALMFG&0000\7&11A88E8E&0&000000000000_0000000C
// to this: BTHENUM\{00001101-0000-1000-8000-00805F9B34FB}_LOCALMFG&0000
try
{
// split by "\" and take 2 elements
PNPDeviceIDShort = string.Join($"\\", pnpdeviceid.Split('\\').Take(2));
}
// todo: check if it can be split instead of using Exception
catch (Exception)
{
// or just take 32 characters if split by "\" is impossible
PNPDeviceIDShort = pnpdeviceid.Substring(0, 32) + "...";
}
}
/// <summary>
/// COM port name, "COM3" for example
/// </summary>
public string Name { get; }
/// <summary>
/// COM port caption from device manager
/// "Intel(R) Active Management Technology - SOL (COM3)" for example
/// </summary>
public string Caption { get; }
/// <summary>
/// PNPDeviceID from device manager
/// "PCI\VEN_8086&DEV_A13D&SUBSYS_224D17AA&REV_31\3&11583659&0&B3" for example
/// </summary>
public string PNPDeviceID { get; }
/// <summary>
/// Shorter version of PNPDeviceID
/// "PCI\VEN_8086&DEV_A13D&SUBSYS_224D17AA&REV_31" for example
/// </summary>
public string PNPDeviceIDShort { get; }
/// <summary>
/// Comparer required to sort by COM port properly (number as number, not string (COM3 before COM21))
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public int CompareTo(object obj)
{
try
{
int a, b;
string sa, sb;
sa = Regex.Replace(Name, "[^0-9.]", "");
sb = Regex.Replace(((SerialPortInfo)obj).Name, "[^0-9.]", "");
if (!int.TryParse(sa, out a))
throw new ArgumentException(nameof(SerialPortInfo) + ": Cannot convert {0} to int32", sa);
if (!int.TryParse(sb, out b))
throw new ArgumentException(nameof(SerialPortInfo) + ": Cannot convert {0} to int32", sb);
return a.CompareTo(b);
}
catch (Exception)
{
return 0;
}
}
public override string ToString()
{
return string.Join(Environment.NewLine, Name, Caption, PNPDeviceID);
}
}
}
}
I'm using it to show detailed serial port lists like this: