使用 Windows API 枚举端口时如何获取扩展端口信息

How to get extended port information when enumerating ports using Windows API

我正在使用一些遗留代码来枚举我机器上的端口:

#include <windows.h>
#include <devguid.h>
#include <setupapi.h>

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

bool GetTextProperty( std::string& sProperty, 
                      HDEVINFO dev, 
                      _SP_DEVINFO_DATA* pDeviceInfoData,
                      DWORD prop )
{
    char szBuf[MAX_PATH];
    DWORD iPropertySize = 0;
    if (SetupDiGetDeviceRegistryProperty(dev, pDeviceInfoData,
                                         prop, 0L, (PBYTE) szBuf, MAX_PATH, &iPropertySize))
    {
        sProperty = szBuf;
        assert( iPropertySize >= sProperty.size() + 1 );
        return true;
    }     
    return false;
}

inline bool readRegistryKeyValue( HKEY hKey, const std::string& key, std::string& value )
{
    bool res = false;
    CHAR szBuffer[512];
    DWORD dwBufferSize = sizeof(szBuffer);
    ULONG nError = RegQueryValueEx(hKey, key.c_str(), 0, NULL, (LPBYTE)szBuffer, &dwBufferSize);
    if (ERROR_SUCCESS == nError)
    {
        value = szBuffer;
        res = true;
    }
    return res;
}

void ListPorts()
{
    HDEVINFO        hDevInfo;
    SP_DEVINFO_DATA DeviceInfoData;
    DWORD           i;

    hDevInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_PORTS, 0L, 0L, DIGCF_PRESENT);
    if ( hDevInfo == INVALID_HANDLE_VALUE )
    {
        //Medoc_ReportError(MEDOC_ERROR_HARDWARE_DRIVER_API_FAILED,
        //                  &hDevInfo, sizeof hDevInfo);
        assert( false );
    }
    else
    {
        // Enumerate through all devices in Set.
        DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
        for (i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData) != 0; i++)
        {
            char szBuf[MAX_PATH];
            short wImageIdx   = 0;
            short wItem       = 0;
            DWORD iPropertySize;

            if (SetupDiGetDeviceRegistryProperty(hDevInfo, &DeviceInfoData,
                                                 SPDRP_FRIENDLYNAME, 0L, (PBYTE) szBuf, MAX_PATH, &iPropertySize))
            {
                std::cout << "Smart name: " << szBuf << std::endl;

                HKEY hKey = SetupDiOpenDevRegKey(
                                hDevInfo,
                                &DeviceInfoData,
                                DICS_FLAG_GLOBAL,
                                0,
                                DIREG_DEV,
                                KEY_READ );
                if ( hKey )
                {
                    std::string portName;
                    readRegistryKeyValue( hKey, "PortName", portName );
                    std::cout << "Port name: " << szBuf << std::endl;

                    for ( DWORD prop = 0; prop != SPDRP_MAXIMUM_PROPERTY; ++prop )
                    {
                        std::string temp;
                        GetTextProperty( temp, hDevInfo, &DeviceInfoData, prop );
                        std::cout << prop << " : " << temp << std::endl;
                    }

                    RegCloseKey(hKey);
                }
            }            
        }
    }

    // Cleanup
    SetupDiDestroyDeviceInfoList(hDevInfo);
}

int main( int argc, char* argv[] )
{
    ListPorts();
    return 0;
}

除其他信息外,这使我可以访问端口名称 (COM*)、类型(例如 FTDI)、VIDPID...

然而,当我插入许多基于 FTDI 芯片的不同设备时,它们都具有相同的信息(SPDRP_HARDWAREID 属性报告 FTDIBUS\COMPORT&VID_0403&PID_6015FTDIBUS\COMPORT&VID_0403&PID_6010)。所以我分不清谁是谁。

当我使用 USB 嗅探器 ("Device Monitoring Studio") 时,这个嗅探器能够报告更多相关信息而无需与端口建立任何连接:

是否可以通过Windows API访问此类扩展信息,以区分使用相同FTDI芯片的多个设备的名称?或者我必须使用 FTDI 驱动程序 API 来实现吗?

在 Ben Voigt 和 Simon Mourier 的帮助下,我实现了这个,下面是一段代码:

// More includes:
#include <initguid.h>
#include <devpkey.h>
#include <cfgmgr32.h>
// A new dependency:
#pragma comment (lib, "Cfgmgr32.lib")

bool GetDeviceProperty( const std::string& what, 
                        DEVINST dev, 
                        DEVPROPKEY prop )
{
    char szDeviceBuf[MAX_PATH];
    DEVPROPTYPE type;
    ULONG iDevicePropertySize = MAX_PATH;
    if ( CM_Get_DevNode_PropertyW(dev,
                                  &prop,
                                  &type,
                                  (PBYTE) szDeviceBuf,
                                  &iDevicePropertySize,
                                  0) == CR_SUCCESS )
    {
        wchar_t* txt = (wchar_t*) szDeviceBuf;
        std::wstring ws(txt);
        std::cout << what << " : " << std::string(ws.begin(), ws.end()) << std::endl;
        return true;
    }
    else
    {
        return false;
    }
}

void ListPorts()
{
        ...

                DEVINST devInstParent;
                auto status = CM_Get_Parent(&devInstParent, DeviceInfoData.DevInst, 0);
                if (status == CR_SUCCESS) 
                {
                    ShowDeviceProperty( "Bus reported device description", devInstParent, DEVPKEY_Device_BusReportedDeviceDesc );
                    ShowDeviceProperty( "Device description", devInstParent, DEVPKEY_Device_DeviceDesc );
                }
                else 
                {
                    continue;
                }

        ...