11

I have the following serial ports listed in my devicemanager:

  • COM3
  • COM4 (BT)
  • COM5 (BT)
  • COM6 (GlobeTrotter MO67xx - Control Interface)
  • COM7 (GlobeTrotter MO67xx - GPS Control Interface)
  • COM8 (GlobeTrotter MO67xx - GPS Data Interface)
  • COM9 (GlobeTrotter MO67xx - Diagnostics Interface)
  • COM11 (USB Serial Port)
  • COM12 (USB Serial Port)
  • COM45 (SUNIX COM Port)
  • COM46 (SUNIX COM Port)

The SUNIX COM ports are connected via an internal PCI-Card. The USB Serial Port is connected via USB (FDTI-chip) The GlobeTrotter ports are from a GlobeTrotter device connected via USB. There are also a modem, a USB-device and a network device listed for this modem.

So I have several different sources of serial ports.

All I want to do is to get a list containing all those ports using WMI.

For my tests I am using WMI Code Creator

Test 1:

root\CIMV2; Query: SELECT * FROM Win32_SerialPort only returns the following serial ports:

  • COM3
  • COM4
  • COM5

Test 2:

root\WMI; Query: SELECT * FROM MSSerial_PortName only returns the following serial ports:

  • COM3
  • COM11
  • COM12
  • COM45
  • COM45

How can I get a complete list of serial ports?

AlexS
  • 5,295
  • 3
  • 38
  • 54

4 Answers4

8

I found the solution.

The following query (root\CIMV2) gets the requested results:

SELECT * FROM Win32_PnPEntity WHERE ClassGuid="{4d36e978-e325-11ce-bfc1-08002be10318}"

Update

This answer is pretty old now. Ehen I asked it I still had to consider WinXP and was using Windows7. Since I don't deal with serial ports any more, I can't give any new information on that issue. At that time this solution reported all ports that the devicemanager was showing. But I know listing serial ports is not that easy so this answer might not be correct in all scenarios.

AlexS
  • 5,295
  • 3
  • 38
  • 54
  • 4
    I found this to be insufficient. I've had very good luck with SELECT * FROM Win32_PnPEntity WHERE Name LIKE '%COM%' instead. – Benjamin Sep 18 '14 at 20:26
  • @ginkner I what way has it been insufficient? Did the results differ? As I mentioned in my question I tested it using various sources of serial ports and it worked. The query should also be more performant than a `like`-clause. – AlexS Sep 19 '14 at 11:07
  • 1
    It didn't return a couple ports I had plugged in, and it doesn't return some virtual serial ports, which are fairly important. Not all serial ports have "COM" in the name either, but it seems to work better than the ClassGuid selection. – Benjamin Sep 24 '14 at 01:38
  • @ginkner Are the devices, that are not listed by the query displayed by the devicemanager (serial ports)? Which operating system are you using? Maybe you should provide a list of devices that are not enumerated by the query. If you allow I would integrate this into my question and answer then. – AlexS Sep 24 '14 at 09:48
  • The GUID approach includes printer/LPT ports. – stefanct Nov 06 '15 at 16:48
4

In my case, I have physical serial ports, USB serial ports, and com0com virtual serial ports. I need both the full names and COM port addresses.

The query suggested in this answer does not find com0com ports. The query suggested in this answer requires Administrator priviledges.

SELECT * FROM Win32_PnPEntity find all devices. It returns physical devices like this, and address can be parsed from Caption:

Serial Port for Barcode Scanner (COM13)

However, for com0com ports Caption is like this (no address):

com0com - serial port emulator

SELECT * FROM Win32_SerialPort returns addresses (DeviceID), as well as full names (Name). However, it only finds physical serial ports and com0com ports, not USB serial ports.

So in the end, I need two WMI calls: SELECT * FROM Win32_SerialPort (address is DeviceID) and SELECT * FROM Win32_PnPEntity WHERE Name LIKE '%(COM%' (address can be parsed from Caption). I have narrowed down the Win32_PnPEntity call, because it only needs to find devices that were not found in the first call.

This C++ code can be used to find all serial ports:

// Return list of serial ports as (number, name)
std::map<int, std::wstring> enumerateSerialPorts()
{
    std::map<int, std::wstring> result;

    HRESULT hres;

    hres = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
    if (SUCCEEDED(hres) || hres == RPC_E_CHANGED_MODE) {
        hres =  CoInitializeSecurity(
            NULL,
            -1,                          // COM authentication
            NULL,                        // Authentication services
            NULL,                        // Reserved
            RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication
            RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
            NULL,                        // Authentication info
            EOAC_NONE,                   // Additional capabilities
            NULL                         // Reserved
            );

        if (SUCCEEDED(hres) || hres == RPC_E_TOO_LATE) {
            IWbemLocator *pLoc = NULL;

            hres = CoCreateInstance(
                CLSID_WbemLocator,
                0,
                CLSCTX_INPROC_SERVER,
                IID_IWbemLocator, (LPVOID *) &pLoc);

            if (SUCCEEDED(hres)) {
                IWbemServices *pSvc = NULL;

                // Connect to the root\cimv2 namespace with
                // the current user and obtain pointer pSvc
                // to make IWbemServices calls.
                hres = pLoc->ConnectServer(
                     bstr_t(L"ROOT\\CIMV2"),  // Object path of WMI namespace
                     NULL,                    // User name. NULL = current user
                     NULL,                    // User password. NULL = current
                     0,                       // Locale. NULL indicates current
                     NULL,                    // Security flags.
                     0,                       // Authority (for example, Kerberos)
                     0,                       // Context object
                     &pSvc                    // pointer to IWbemServices proxy
                     );
                if (SUCCEEDED(hres)) {
                    hres = CoSetProxyBlanket(
                       pSvc,                        // Indicates the proxy to set
                       RPC_C_AUTHN_WINNT,           // RPC_C_AUTHN_xxx
                       RPC_C_AUTHZ_NONE,            // RPC_C_AUTHZ_xxx
                       NULL,                        // Server principal name
                       RPC_C_AUTHN_LEVEL_CALL,      // RPC_C_AUTHN_LEVEL_xxx
                       RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
                       NULL,                        // client identity
                       EOAC_NONE                    // proxy capabilities
                    );
                    if (SUCCEEDED(hres)) {
                        // Use Win32_PnPEntity to find actual serial ports and USB-SerialPort devices
                        // This is done first, because it also finds some com0com devices, but names are worse
                        IEnumWbemClassObject* pEnumerator = NULL;
                        hres = pSvc->ExecQuery(
                            bstr_t(L"WQL"),
                            bstr_t(L"SELECT Name FROM Win32_PnPEntity WHERE Name LIKE '%(COM%'"),
                            WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
                            NULL,
                            &pEnumerator);

                        if (SUCCEEDED(hres)) {
                            constexpr size_t max_ports = 30;
                            IWbemClassObject *pclsObj[max_ports] = {};
                            ULONG uReturn = 0;

                            do {
                                hres = pEnumerator->Next(WBEM_INFINITE, max_ports, pclsObj, &uReturn);
                                if (SUCCEEDED(hres)) {
                                    for (ULONG jj = 0; jj < uReturn; jj++) {
                                        VARIANT vtProp;
                                        pclsObj[jj]->Get(L"Name", 0, &vtProp, 0, 0);

                                        // Name should be for example "Serial Port for Barcode Scanner (COM13)"
                                        const std::wstring deviceName = vtProp.bstrVal;
                                        const std::wstring prefix = L"(COM";
                                        size_t ind = deviceName.find(prefix);
                                        if (ind != std::wstring::npos) {
                                            std::wstring nbr;
                                            for (size_t i = ind + prefix.length();
                                                i < deviceName.length() && isdigit(deviceName[i]); i++)
                                            {
                                                nbr += deviceName[i];
                                            }
                                            try {
                                                const int portNumber = boost::lexical_cast<int>(nbr);
                                                result[portNumber] = deviceName;
                                            }
                                            catch (...) {}
                                        }
                                        VariantClear(&vtProp);

                                        pclsObj[jj]->Release();
                                    }
                                }
                            } while (hres == WBEM_S_NO_ERROR);
                            pEnumerator->Release();
                        }

                        // Use Win32_SerialPort to find physical ports and com0com virtual ports
                        // This is more reliable, because address doesn't have to be parsed from the name
                        pEnumerator = NULL;
                        hres = pSvc->ExecQuery(
                            bstr_t(L"WQL"),
                            bstr_t(L"SELECT DeviceID, Name FROM Win32_SerialPort"),
                            WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
                            NULL,
                            &pEnumerator);

                        if (SUCCEEDED(hres)) {
                            constexpr size_t max_ports = 30;
                            IWbemClassObject *pclsObj[max_ports] = {};
                            ULONG uReturn = 0;

                            do {
                                hres = pEnumerator->Next(WBEM_INFINITE, max_ports, pclsObj, &uReturn);
                                if (SUCCEEDED(hres)) {
                                    for (ULONG jj = 0; jj < uReturn; jj++) {
                                        VARIANT vtProp1, vtProp2;
                                        pclsObj[jj]->Get(L"DeviceID", 0, &vtProp1, 0, 0);
                                        pclsObj[jj]->Get(L"Name", 0, &vtProp2, 0, 0);

                                        const std::wstring deviceID = vtProp1.bstrVal;
                                        if (deviceID.substr(0, 3) == L"COM") {
                                            const int portNumber = boost::lexical_cast<int>(deviceID.substr(3));
                                            const std::wstring deviceName = vtProp2.bstrVal;
                                            result[portNumber] = deviceName;
                                        }
                                        VariantClear(&vtProp1);
                                        VariantClear(&vtProp2);

                                        pclsObj[jj]->Release();
                                    }
                                }
                            } while (hres == WBEM_S_NO_ERROR);
                            pEnumerator->Release();
                        }
                    }
                    pSvc->Release();
                }
                pLoc->Release();
            }
        }
        CoUninitialize();
    }
    if (FAILED(hres)) {
        std::stringstream ss;
        ss << "Enumerating serial ports failed. Error code: " << int(hres);
        throw std::runtime_error(ss.str());
    }

    return result;
}
VLL
  • 9,634
  • 1
  • 29
  • 54
1

The Win32_SerialPort class used in this article reports the physical com ports, if you wanna enumerate all the serial ports including the USB-Serial/COM ports, you must use the MSSerial_PortName class located in the root\wmi namespace.

Also try these classes located in the same namespace

  • MSSerial_CommInfo
  • MSSerial_CommProperties
  • MSSerial_HardwareConfiguration
  • MSSerial_PerformanceInformation

Note : If you want to know the properties and methods of this class you can use the WMI Delphi Code Creator.

RRUZ
  • 134,889
  • 20
  • 356
  • 483
  • This returns only 3 instances (the 2 SUNIX included), but all Bluetooth-SerialPorts are missing. – AlexS Nov 08 '13 at 09:33
  • This doesn't work, because accessing root\wmi requires administrator access for modern windows systems. – Benjamin Sep 18 '14 at 20:27
1

I had a similar issues trying to have an application locate the COM port for a USB Serial device.

By using the scope \\localhost\root\CIMV2 for the query SELECT * FROM Win32_PnPEntity WHERE ConfigManagerErrorCode = 0, the application was able to find COM ports via each returned object's caption or locate the exact port by checking the caption for the device name.

ManagementObjectSearcher comPortSearcher = new ManagementObjectSearcher(@"\\localhost\root\CIMV2", "SELECT * FROM Win32_PnPEntity WHERE ConfigManagerErrorCode = 0");

        using (comPortSearcher)
        {
            string caption = null;
            foreach (ManagementObject obj in comPortSearcher.Get())
            {
                if (obj != null)
                {
                    object captionObj = obj["Caption"];
                    if (captionObj != null)
                    {
                        caption = captionObj.ToString();
                            if (caption.Contains("CH340"))
                            {
                                _currentSerialSettings.PortName = caption.Substring(caption.LastIndexOf("(COM")).Replace("(", string.Empty).Replace(")", string.Empty);
                            }
                    }
                }
            }
        }

The parsing code was found at [C#] How to programmatically find a COM port by friendly name