Question

I'm looking for a robust way to list the available serial (COM) ports on a Windows machine. There's this post about using WMI, but I would like something less .NET specific - I want to get the list of ports in a Python or a C++ program, without .NET.

I currently know of two other approaches:

  1. Reading the information in the HARDWARE\\DEVICEMAP\\SERIALCOMM registry key. This looks like a great option, but is it robust? I can't find a guarantee online or in MSDN that this registry cell indeed always holds the full list of available ports.

  2. Tryint to call CreateFile on COMN with N a number from 1 to something. This isn't good enough, because some COM ports aren't named COMN. For example, some virtual COM ports created are named CSNA0, CSNB0, and so on, so I wouldn't rely on this method.

Any other methods/ideas/experience to share?

Edit: by the way, here's a simple Python implementation of reading the port names from registry:

import _winreg as winreg
import itertools


def enumerate_serial_ports():
    """ Uses the Win32 registry to return a iterator of serial 
        (COM) ports existing on this computer.


    """
    path = 'HARDWARE\\DEVICEMAP\\SERIALCOMM'
    try:
        key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path)
    except WindowsError:
        raise IterationError

    for i in itertools.count():
        try:
            val = winreg.EnumValue(key, i)
            yield (str(val[1]), str(val[0]))
        except EnvironmentError:
            break
Was it helpful?

Solution

Several options are available:

  1. Call QueryDosDevice with a NULL lpDeviceName to list all DOS devices. Then use CreateFile and GetCommConfig with each device name in turn to figure out whether it's a serial port.

  2. Call SetupDiGetClassDevs with a ClassGuid of GUID_DEVINTERFACE_COMPORT.

  3. WMI is also available to C/C++ programs.

There's some conversation on the win32 newsgroup and a CodeProject, er, project.

OTHER TIPS

Using pySerial with Python:

import serial.tools.list_ports


ports = list(serial.tools.list_ports.comports())
for p in ports:
    print p

The PySerial project provides a couple of solutions.

I just created the following, based on reading through the C++ source to EnumSerialPorts and seeing the function GetDefaultCommConfig(). It looked like the simplest method using simple ANSI C and a single API call for each possible COM port.

#include <stdio.h>
#include <windows.h>
#include <winbase.h>

BOOL COM_exists( int port)
{
    char buffer[7];
    COMMCONFIG CommConfig;
    DWORD size;

    if (! (1 <= port && port <= 255))
    {
        return FALSE;
    }

    snprintf( buffer, sizeof buffer, "COM%d", port);
    size = sizeof CommConfig;

    // COM port exists if GetDefaultCommConfig returns TRUE
    // or changes <size> to indicate COMMCONFIG buffer too small.
    return (GetDefaultCommConfig( buffer, &CommConfig, &size)
                                                    || size > sizeof CommConfig);
}

int main()
{
    int i;

    for (i = 1; i < 256; ++i)
    {
        if (COM_exists( i))
        {
            printf( "COM%d exists\n", i);
        }
    }

    return 0;
}

This is definitely fairly late, but it proved helpful to me!

http://eli.thegreenplace.net/2009/07/31/listing-all-serial-ports-on-windows-with-python/

Particularly this example:

import re

def full_port_name(portname):
    """ Given a port-name (of the form COM7,
        COM12, CNCA0, etc.) returns a full
        name suitable for opening with the
        Serial class.
    """
    m = re.match('^COM(\d+)$', portname)
    if m and int(m.group(1)) < 10:
    return portname
return '\\\\.\\' + portname

There's an example in the pyserial distro now that does this called scanwin32.py

http://pyserial.sourcearchive.com/documentation/2.5/scanwin32_8py_source.html

I think WMI is the way to go since it's pretty easy to get going and it has minimal code. It saves you from having to dig inside the registry and gives you some guarantee that it will work for more general situations in the future.

You can install everything needed with pip install pypiwin32 WMI and it works out-of-the-box.

Code

import wmi

query = "SELECT * FROM Win32_PnPEntity WHERE Name LIKE '%(COM%)'"
coms  = wmi.WMI().query(query)

for com in coms:
    print(com.Name)

Output

Communications Port (COM1)
mbed Serial Port (COM3)
mbed Serial Port (COM5)

My guess is that your serial port is some sort of Plug 'n Play so this should work fine. For some reason Win32_SerialPort doesn't work for all ports.

Nowadays there's a Powershell one-liner for that.

[System.IO.Ports.SerialPort]::GetPortNames()
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top