2025 Master COM Ports with C++ | Full VSCode Development Guide


In the field of Internet of Things (IoT), the COM port (serial port) remains a crucial method for data transmission between many devices. While modern hardware often relies on more advanced interfaces like USB, COM ports still play a vital role in embedded systemsindustrial automation, and legacy hardware.
In this article, we’ll demonstrate how to develop a cross-platform COM port listing tool using C++, and explore how this can be extended for IoT applications—helping you manage and monitor your devices more efficiently.

COM Ports

Why Choose C++ for Development?

High Performance:
C++ is a compiled language that generates highly efficient machine code, making it suitable for applications requiring high performance and real-time response. Compared to interpreted languages like Python, C++ is much faster—especially when dealing with large datasets, game development, or embedded systems where performance is critical.

Resource Control:
C++ allows developers precise control over memory, which is crucial for low-level system tuning or hardware interaction (such as drivers or embedded systems). In contrast, languages like Java and C# rely on garbage collection and offer less control over memory management.

What is a COM Port?

COM Port (short for Communication Port) is a hardware interface used for serial communication between a computer and external devices. It is commonly used for data transmission and was one of the most prevalent serial interfaces in older computers. COM ports are also referred to as serial ports or RS-232 ports.

Even though USB has largely replaced COM ports as the standard connection interface, COM ports are still widely used in certain domains—particularly in embedded systemsindustrial equipmentlegacy hardware, and some debugging applications.

Development Environment

Before getting started, make sure the following tools are installed:

Create a C++ Project in VSCode

Inside the folder, create the following file structure:

Create a folder named serial_port_lister and open it in VSCode.

serial_port_lister/
├── CMakeLists.txt   # CMake build configuration
└── main.cpp         # Code

COM Port Listing Code

Below is the C++ code that lists COM ports across different operating systems:

#include <iostream>
#include <vector>
#include <string>

#ifdef _WIN32
#include <windows.h>
#include <setupapi.h>
#include <devguid.h>
#include <regstr.h>
#pragma comment(lib, "setupapi.lib")

#elif defined(__APPLE__)
#include <dirent.h>
#include <sys/stat.h>

#else // Linux
#include <dirent.h>
#include <sys/stat.h>
#endif

std::vector<std::string> listSerialPorts() {
    std::vector<std::string> ports;

#ifdef _WIN32
    // Windows implementation
    HDEVINFO hDevInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_PORTS, NULL, NULL, DIGCF_PRESENT);
    if (hDevInfo == INVALID_HANDLE_VALUE) {
        return ports;
    }

    SP_DEVINFO_DATA deviceInfoData;
    deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);

    for (DWORD i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &deviceInfoData); i++) {
        char buffer[256];
        DWORD bufferSize = sizeof(buffer);

        if (SetupDiGetDeviceRegistryPropertyA(
            hDevInfo, &deviceInfoData, SPDRP_FRIENDLYNAME,
            NULL, (PBYTE)buffer, bufferSize, &bufferSize)) {
            ports.push_back(buffer);
        }
    }

    SetupDiDestroyDeviceInfoList(hDevInfo);

#elif defined(__APPLE__)
    // macOS implementation
    DIR* dir = opendir("/dev");
    if (dir) {
        struct dirent* entry;
        while ((entry = readdir(dir)) != nullptr) {
            std::string name = entry->d_name;
            if (name.find("cu.") == 0 || name.find("tty.") == 0) {
                ports.push_back("/dev/" + name);
            }
        }
        closedir(dir);
    }

#else
    // Linux implementation
    DIR* dir = opendir("/dev");
    if (dir) {
        struct dirent* entry;
        while ((entry = readdir(dir)) != nullptr) {
            std::string name = entry->d_name;
            if (name.find("ttyS") == 0 || name.find("ttyUSB") == 0 || 
                name.find("ttyACM") == 0 || name.find("ttyAMA") == 0) {
                ports.push_back("/dev/" + name);
            }
        }
        closedir(dir);
    }
#endif

    return ports;
}

int main() {
    std::cout << "Available COM Ports:" << std::endl;
    
    auto ports = listSerialPorts();
    
    if (ports.empty()) {
        std::cout << "No COM ports found." << std::endl;
    } else {
        for (const auto& port : ports) {
            std::cout << " - " << port << std::endl;
        }
    }
    
    return 0;
}

Configure CMakeLists.txt

To build the project using CMake, you need to provide a CMakeLists.txt file that describes how the project should be compiled and linked.

cmake_minimum_required(VERSION 3.10)
project(SerialPortLister VERSION 1.0.0)

# Basic settings
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# Platform-specific settings
if(WIN32)
    add_definitions(-DWIN32_LEAN_AND_MEAN)
    find_library(SETUPAPI_LIB setupapi)
    message(STATUS "Found Windows setupapi.lib")
elseif(APPLE)
    message(STATUS "Configuring for macOS")
else()
    message(STATUS "Configuring for Linux")
endif()

# Main program
add_executable(${PROJECT_NAME} 
    main.cpp
)

# Link libraries
if(WIN32)
    target_link_libraries(${PROJECT_NAME} PRIVATE ${SETUPAPI_LIB})
endif()

# Installation settings (optional)
install(TARGETS ${PROJECT_NAME}
    RUNTIME DESTINATION bin
)

# Packaging settings (optional)
include(InstallRequiredSystemLibraries)
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)

Build and Run

In VSCode, press Ctrl (or Cmd) + Shift + P, then type and select “CMake: Build” to compile the project.
Once built, run the program (example for macOS):

./SerialPortLister

Output Example:

Available COM Ports:
 - /dev/cu.usbserial-1234
 - /dev/tty.usbserial-5678

Conclusion

This simple cross-platform application demonstrates how to list serial communication ports on different operating systems. By using conditional compilation, we can provide platform-specific implementations while maintaining a unified interface. You can further extend this program by adding features such as: Real-time port monitoring, A graphical user interface (GUI), Detailed port info (manufacturer, device ID, etc.).