10

There are several ways to list serial ports under Windows but I'm not sure what is the proper way: the way that does detect all serial ports that are available.

One good code example is http://www.naughter.com/enumser.html - where there are 9 (nine!) ways of enumerating serial devices.

The question is: what is the optimal way of doing it.

Requirements:

  • to not open ports in order to check if they are available.
  • to be able to detect ports with different names than COMx.
  • to work on Windows XP SP2 or above
David Sykes
  • 48,469
  • 17
  • 71
  • 80
sorin
  • 161,544
  • 178
  • 535
  • 806

7 Answers7

10
void SelectComPort() //added function to find the present serial 
{

    TCHAR lpTargetPath[5000]; // buffer to store the path of the COMPORTS
    DWORD test;
    bool gotPort=0; // in case the port is not found

    for(int i=0; i<255; i++) // checking ports from COM0 to COM255
    {
        CString str;
        str.Format(_T("%d"),i);
        CString ComName=CString("COM") + CString(str); // converting to COM0, COM1, COM2

        test = QueryDosDevice(ComName, (LPSTR)lpTargetPath, 5000);

            // Test the return value and error if any
        if(test!=0) //QueryDosDevice returns zero if it didn't find an object
        {
            m_MyPort.AddString((CString)ComName); // add to the ComboBox
            gotPort=1; // found port
        }

        if(::GetLastError()==ERROR_INSUFFICIENT_BUFFER)
        {
            lpTargetPath[10000]; // in case the buffer got filled, increase size of the buffer.
            continue;
        }

    }

    if(!gotPort) // if not port
    m_MyPort.AddString((CString)"No Active Ports Found"); // to display error message incase no ports found

}
BenMorel
  • 34,448
  • 50
  • 182
  • 322
  • 4
    This line does not do anything: lpTargetPath[10000]; – HS. Aug 30 '18 at 07:38
  • Would prefer to work with dynamic lpTargetPath, then in the INSUFFICIENT_BUFFER branch: lpTargetPath = realloc(lpTargetPath, bufSize); bufSize*=2; **i--;** – Sam Ginrich Mar 15 '22 at 01:19
5

If you can access the registry, the HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM key contains a list of COM ports Windows currently supports (in some cases, this information may be stale/incorrect; like, I suspect, when a plug & play device providing serial ports has not completed detection/installation or has been recently removed).

This is the way .NET Framework's SerialPort.GetPortNames() method reports available COM ports, and the above information is derived from the linked page.

Doc
  • 343
  • 3
  • 13
5

Modified @Dženan answer to use wide characters and returning list of ints

#include <string>
#include <list>

list<int> getAvailablePorts()
{
    wchar_t lpTargetPath[5000]; // buffer to store the path of the COM PORTS
    list<int> portList;

    for (int i = 0; i < 255; i++) // checking ports from COM0 to COM255
    {
        wstring str = L"COM" + to_wstring(i); // converting to COM0, COM1, COM2
        DWORD res = QueryDosDevice(str.c_str(), lpTargetPath, 5000);

        // Test the return value and error if any
        if (res != 0) //QueryDosDevice returns zero if it didn't find an object
        {
            portList.push_back(i);
            //std::cout << str << ": " << lpTargetPath << std::endl;
        }
        if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER)
        {
        }
    }
    return portList;
}
4

This is a modernized version of @michael-jacob-mathew's answer:

#include <iostream>
#include <string>
#include <Windows.h>

bool SelectComPort() //added function to find the present serial 
{
    char lpTargetPath[5000]; // buffer to store the path of the COMPORTS
    bool gotPort = false; // in case the port is not found

    for (int i = 0; i < 255; i++) // checking ports from COM0 to COM255
    {
        std::string str = "COM" + std::to_string(i); // converting to COM0, COM1, COM2
        DWORD test = QueryDosDevice(str.c_str(), lpTargetPath, 5000);

        // Test the return value and error if any
        if (test != 0) //QueryDosDevice returns zero if it didn't find an object
        {
            std::cout << str << ": " << lpTargetPath << std::endl;
            gotPort = true;
        }

        if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER)
        {
        }
    }

    return gotPort;
}

It produces the following output on my computer:

COM1: \Device\Serial0
COM3: \Device\VCP0
Dženan
  • 3,329
  • 3
  • 31
  • 44
3

Serial ports are very simple devices, dating from the stone age of computing hardware. They don't support Plug & Play, there is no way to tell that somebody plugged in a device. The only thing you can do is discover what ports are available, the SerialPort.GetPortNames() returns the list. Some USB emulators can generate a descriptive name to go with the port name, you can discover those with WMI, Win32_SerialPort class.

None of which helps you discover what COM port is connected to a particular device. Only a human knows, she physically plugged the cable in the connector. You'll need to provide a config UI that lets the user select the port number. A combo box gets the job done. Save the selection in your config data, it is very likely that the device is still connected to the same port the next time your program starts.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • I use SerialPort.GetPortNames(). – dbasnett Apr 22 '10 at 12:00
  • 6
    Serial ports are so under-rated! Sometimes I don't want any of that complicated plug & play malarky.. I just want to plug in a cable and type. Long live RS232! – Lightness Races in Orbit Sep 17 '15 at 12:16
  • @LightnessRacesinOrbit Com ports are fine until you're in the position where you have to try to detect when one has been added, then they're hell. – Pharap Aug 06 '17 at 05:19
  • There is a way to detect that a new COM port is available using RegisterDeviceNotificationA (https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerdevicenotificationa?redirectedfrom=MSDN) – Pedro Ferreira Oct 07 '22 at 15:18
0

You can check the windows registry base to list all COM ports. Here is my code > github file

Vladivarius
  • 498
  • 1
  • 3
  • 14
-3
CUIntArray ports;
EnumerateSerialPorts(ports);

for (int i = 0; i<ports.GetSize(); i++)
{
    CString str;
    str.Format(_T("COM%d"), ports.ElementAt(i));
    m_ctlPort.AddString(str);
}
Nathaniel Ford
  • 20,545
  • 20
  • 91
  • 102