代码之家  ›  专栏  ›  技术社区  ›  GrahamS

如何获取Win32中可用串行端口的列表?

  •  47
  • GrahamS  · 技术社区  · 17 年前

    我有一些遗留代码,通过调用 EnumPorts() 函数,然后筛选以“COM”开头的端口名。

    出于测试目的,如果我可以将此代码与以下内容结合使用,这将非常有用 com0com ,它提供成对的虚拟COM端口作为空调制解调器循环在一起。

    但是,服务器找不到com0com端口 枚举端口() 函数(即使不过滤“COM”)。HyperTerminal和SysInternals PortMon都可以看到它们,因此我确信它安装正确。

    那么,是否有其他Win32函数提供可用串行端口的最终列表?

    5 回复  |  直到 17 年前
        1
  •  88
  •   GrahamS    9 年前

    这个 EnumSerialPorts v1.20 Nick D建议使用 九 列出串行端口的不同方法!我们当然不缺少选择,尽管结果似乎各不相同。

    为了省去其他人的麻烦,我将在这里列出他们,并指出他们成功地找到了 com0com

    1. 创建文件(“COM”+1->255) 正如Wael Dalloul所建议的
      找到com0com端口,耗时234ms。


    2. 找到com0com端口,耗时0毫秒。

    3. GetDefaultCommConfig(“COM”+1->255)

    4. 使用对SETUPAPI.DLL的调用“SetupAPI1”


    5. 未找到com0com端口,报告“友好名称”,耗时32毫秒。

    6. 枚举端口()
      报告了一些非COM端口,未找到com0com端口,耗时15毫秒。

    7. 使用WMI调用


    8. 迭代注册表项HKEY\U LOCAL\U MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM
      找到com0com端口,耗时0毫秒。这显然是什么 SysInternals PortMon 使用。

    基于这些结果,我认为WMI方法可能最适合我的要求,因为它相对较快,而且它还提供了友好的名称(例如,“通信端口(COM1)”,“com0com-串行端口模拟器”)。

        2
  •  11
  •   Nick Dandoulakis    17 年前
        3
  •  4
  •   Wael Dalloul    9 年前

    要打开端口,请使用 创建文件 应用程序编程接口:

    HANDLE Port = CreateFile(
                      "\\\\.\\COM1",
                      GENERIC_READ | GENERIC_WRITE,
                      0,
                      NULL,
                      OPEN_EXISTING,
                      FILE_ATTRIBUTE_NORMAL,
                      NULL);
    

    然后检查结果。

        4
  •  3
  •   VLL Mohamed El-Nakeep    5 年前

    正如公认的答案所建议的,我使用WMI调用。 SELECT * FROM Win32_PnPEntity Caption

    Serial Port for Barcode Scanner (COM13)
    

    但是,对于com0com端口 说明文字 是这样的(没有地址):

    com0com - serial port emulator
    

    SELECT * FROM Win32_SerialPort 返回地址( DeviceID Name )。但是,它只查找物理串行端口和com0com端口,而不查找USB串行端口。

    从Win32_SerialPort中选择* (地址是 )及 SELECT * FROM Win32_PnPEntity WHERE Name LIKE '%(COM%' (地址可以从 说明文字 )1.我已经缩小了范围 Win32_PnPEntity

    这个C++代码可以用来查找所有的串口:

    // 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;
    }
    
        5
  •  1
  •   Gaiger Chen    11 年前

    我重新组织了PJ Notter的 EnumSerialPorts 作为更具可移植性和个性化的表单,这更有用。

    为了更好地兼容,我使用C,而不是C++。

    如果您需要或感兴趣,请访问 the post 在我的博客里。