itas109 / CSerialPort

CSerialPort - lightweight cross-platform serial port library for C++/C/C#/Java/Python/Node.js/Electron
https://blog.csdn.net/itas109/article/details/84282860
Other
710 stars 337 forks source link

some win10 could not enum all avaiable ports with function availablePortInfos() #41

Closed itas109 closed 3 years ago

itas109 commented 3 years ago

Describe the bug some win10 could not enum all avaiable ports with function availablePortInfos()

To Reproduce call function availablePortInfos()

Desktop (please complete the following information):

Additional context Add any other context about the problem here.

itas109 commented 3 years ago

compile with mingw730_64(Qt5.12.9)

this application listTest.exe can enum port info with five method.

listPortTest

listPortTest-20201210.zip

itas109 commented 3 years ago

Maybe compiler or Windows 10 system version problem

some win10 version with this problem :

windows 10 Home Edition CN 10.0.18363 
windows 10 ProfessionalEdition CN 10.0.18363 
itas109 commented 3 years ago

Maybe bluetooth problem.

设备管理器中有蓝牙转的串口会出现只能枚举部分串口的情况

Standard Serial over Bluetooth link(COM5)
蓝牙链接上的标准串行(COM5)
itas109 commented 3 years ago

also maybe SetupDiGetClassDevsW use error.

v4.1.0

 // Return only devices that are currently present in a system
// The GUID_DEVINTERFACE_COMPORT device interface class is defined for COM ports. GUID
// {86E0D1E0-8089-11D0-9CE4-08003E301F73}
SetupDiGetClassDevs(&GUID_DEVINTERFACE_COMPORT, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

https://docs.microsoft.com/zh-cn/windows/win32/api/setupapi/nf-setupapi-setupdigetclassdevsw

WINSETUPAPI HDEVINFO SetupDiGetClassDevsW(
  const GUID *ClassGuid,
  PCWSTR     Enumerator,
  HWND       hwndParent,
  DWORD      Flags
);
itas109 commented 3 years ago

when has bluetooth serial port.

DEFINE_GUID(GUID_DEVINTERFACE_COMPORT, 0x86e0d1e0L, 0x8089, 0x11d0, 0x9c, 0xe4, 0x08, 0x00, 0x3e, 0x30, 0x1f, 0x73);

  1. use cmake compile

enum success

  1. use CSerialPort\examples\CommNoGui\compile-MinGW.bat compile

enum success

  1. use CSerialPort\examples\CommNoGui\compile-MSVC.bat compile

mfc has same problem

enum error at

SetupDiGetDeviceInterfaceDetail(hDevInfo, &did, pDetData, dwDetDataSize, NULL, &devdata)
GetLastError() return 122

ERROR_INSUFFICIENT_BUFFER

122 (0x7A)

The data area passed to a system call is too small.

122 - 传递到系统调用的数据区太小

when enum bluetooth serial port SetupDiGetDeviceInterfaceDetail return false

当枚举蓝牙的串口时,SetupDiGetDeviceInterfaceDetail 返回错误,使得枚举结束导致枚举不全

DWORD requiredSize = 0;
// first time get buffer size
SetupDiGetDeviceInterfaceDetail(hDevInfo, &did, NULL, NULL, &requiredSize, NULL);
// second time require devdata
SetupDiGetDeviceInterfaceDetail(hDevInfo, &did, pDetData, requiredSize, NULL, &devdata);

use SetupDiEnumDeviceInfo instead of SetupDiGetDeviceInterfaceDetail

itas109 commented 3 years ago

use SetupDiEnumDeviceInfo instead of SetupDiGetDeviceInterfaceDetail is ok.

bool enumDetailsSerialPorts(vector<SerialPortInfo> &portInfoList)
{
    // https://docs.microsoft.com/en-us/windows/win32/api/setupapi/nf-setupapi-setupdienumdeviceinfo

    bool bRet = false;
    SerialPortInfo m_serialPortInfo;

    std::string strFriendlyName;
    std::string strPortName;

    HDEVINFO hDevInfo = INVALID_HANDLE_VALUE;
    SP_DEVICE_INTERFACE_DETAIL_DATA *pDetData = NULL;

    // Return only devices that are currently present in a system
    // The GUID_DEVINTERFACE_COMPORT device interface class is defined for COM ports. GUID
    // {86E0D1E0-8089-11D0-9CE4-08003E301F73}
    hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_COMPORT, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

    if (INVALID_HANDLE_VALUE != hDevInfo)
    {
        SP_DEVINFO_DATA devInfoData;
        // The caller must set DeviceInfoData.cbSize to sizeof(SP_DEVINFO_DATA)
        devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);

        for (DWORD i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &devInfoData); i++)
        {
            // get port name
            TCHAR portName[256];
            HKEY hDevKey = SetupDiOpenDevRegKey(hDevInfo, &devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
            if (INVALID_HANDLE_VALUE != hDevKey)
            {
                DWORD dwCount = 255; // DEV_NAME_MAX_LEN
                RegQueryValueEx(hDevKey, _T("PortName"), NULL, NULL, (BYTE *)portName, &dwCount);
                RegCloseKey(hDevKey);
            }

            // get friendly name
            TCHAR fname[256];
            SetupDiGetDeviceRegistryProperty(hDevInfo, &devInfoData, SPDRP_FRIENDLYNAME, NULL, (PBYTE)fname,
                                             sizeof(fname), NULL);

#ifdef UNICODE
            strPortName = wstringToString(portName);
            strFriendlyName = wstringToString(fname);
#else
            strPortName = std::string(portName);
            strFriendlyName = std::string(fname);
#endif
            // remove (COMxx)
            strFriendlyName = strFriendlyName.substr(0, strFriendlyName.find(("(COM")));

            m_serialPortInfo.portName = strPortName;
            m_serialPortInfo.description = strFriendlyName;
            portInfoList.push_back(m_serialPortInfo);
        }

        if (ERROR_NO_MORE_ITEMS == GetLastError())
        {
            bRet = true; // no more item
        }
    }

    SetupDiDestroyDeviceInfoList(hDevInfo);

    return bRet;
}
itas109 commented 3 years ago

https://docs.microsoft.com/en-us/windows/win32/power/enumerating-battery-devices

this code is also ok. but use SetupDiEnumDeviceInfo instead of SetupDiGetDeviceInterfaceDetail

bool enumDetailsSerialPorts(vector<SerialPortInfo> &portInfoList)
{
    // https://docs.microsoft.com/en-us/windows/win32/api/setupapi/

    bool bRet = false;
    SerialPortInfo m_serialPortInfo;

    std::string strFriendlyName;
    std::string strPortName;

    HDEVINFO hDevInfo = INVALID_HANDLE_VALUE;
    SP_DEVICE_INTERFACE_DETAIL_DATA *pDetData = NULL;

    // Return only devices that are currently present in a system
    // The GUID_DEVINTERFACE_COMPORT device interface class is defined for COM ports. GUID
    // {86E0D1E0-8089-11D0-9CE4-08003E301F73}
    hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_COMPORT, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

    if (INVALID_HANDLE_VALUE != hDevInfo)
    {
        BOOL bOk = TRUE;

        SP_DEVICE_INTERFACE_DATA did = {0};
        did.cbSize = sizeof(did);

        for (DWORD idev = 0; bOk; idev++)
        {
            // enumerates the device interfaces that are contained in a device information set
            bOk = SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &GUID_DEVINTERFACE_COMPORT, idev, &did);
            if (bOk)
            {
                SP_DEVINFO_DATA devdata;
                devdata.cbSize = sizeof(SP_DEVINFO_DATA);
                // get detailed information about an interface
                DWORD requiredSize = 0;
                SetupDiGetDeviceInterfaceDetail(hDevInfo, &did, NULL, NULL, &requiredSize, NULL);

                PSP_DEVICE_INTERFACE_DETAIL_DATA pDetData =
                    (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LPTR, requiredSize);
                pDetData->cbSize = sizeof(*pDetData);

                bOk = SetupDiGetDeviceInterfaceDetail(hDevInfo, &did, pDetData, requiredSize, &requiredSize, &devdata);

                if (bOk)
                {
                    TCHAR fname[256];
                    // retrieves a specified Plug and Play device property (include friendlyname etc.)
                    BOOL bSuccess = SetupDiGetDeviceRegistryProperty(hDevInfo, &devdata, SPDRP_FRIENDLYNAME, NULL,
                                                                     (PBYTE)fname, sizeof(fname), NULL);

                    TCHAR portName[256];
                    // get port name
                    HKEY hDevKey = SetupDiOpenDevRegKey(hDevInfo, &devdata, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
                    if (INVALID_HANDLE_VALUE != hDevKey)
                    {
                        DWORD dwCount = 255; // DEV_NAME_MAX_LEN
                        RegQueryValueEx(hDevKey, _T("PortName"), NULL, NULL, (BYTE *)portName, &dwCount);
                        RegCloseKey(hDevKey);

                        bSuccess &= TRUE;
                    }
                    else
                    {
                        bSuccess &= FALSE;
                    }

                    if (bSuccess)
                    {
#ifdef UNICODE
                        strPortName = wstringToString(portName);
                        strFriendlyName = wstringToString(fname);
#else
                        strPortName = std::string(portName);
                        strFriendlyName = std::string(fname);
#endif
                        // remove (COMxx)
                        strFriendlyName = strFriendlyName.substr(0, strFriendlyName.find(("(COM")));

                        m_serialPortInfo.portName = strPortName;
                        m_serialPortInfo.description = strFriendlyName;
                        portInfoList.push_back(m_serialPortInfo);

                        bRet = true;
                    }
                }
                else
                {
                    bRet = false;
                }
            }
            else if (ERROR_NO_MORE_ITEMS == GetLastError())
            {
                bRet = false; // Enumeration failed
                break;
            }
            else
            {
            }
        }

        SetupDiDestroyDeviceInfoList(hDevInfo);
    }

    return bRet;
}
itas109 commented 3 years ago

CEnumerateSerial v1.40 A C++ class to enumerate serial ports

http://www.naughter.com/enumser.html

EnumSer.zip

Notice: some method is wrong

CommName friendlyName PhysicalDeviceObjectName
COM1 ELTIMA Virtual Serial Port \Device\VSerial7_0
COM2 ELTIMA Virtual Serial Port \Device\VSerial7_1
COM4 Prolific USB-to-Serial Comm Port \Device\ProlificSerial0
COM6 com0com - serial port emulator \Device\com0com11
COM7 com0com - serial port emulator \Device\com0com21
CNCA0 com0com - serial port emulator CNCA0 \Device\com0com10
CNCB0 com0com - serial port emulator CNCB0 \Device\com0com20
CreateFile method reports
COM1
COM2
COM4
COM6
COM7
 Time taken: 391 ms

QueryDosDevice method reports
COM1
COM6
COM2
COM7
COM4
 Time taken: 0 ms

GetDefaultCommConfig method reports
COM1
COM2
COM4
COM6
COM7
 Time taken: 6312 ms

Device Manager (SetupAPI - GUID_DEVINTERFACE_COMPORT) reports
COM6 <com0com - serial port emulator>
COM4 <Prolific USB-to-Serial Comm Port>
COM1 <Virtual Serial Port 7 (Eltima Software)>
COM7 <com0com - serial port emulator>
COM2 <Virtual Serial Port 7 (Eltima Software)>
 Time taken: 16 ms

Device Manager (SetupAPI - Ports Device information set) reports
COM1 <Virtual Serial Port 7 (Eltima Software)>
COM2 <Virtual Serial Port 7 (Eltima Software)>
COM4 <Prolific USB-to-Serial Comm Port>
COM6 <com0com - serial port emulator>
COM7 <com0com - serial port emulator>
 Time taken: 15 ms

EnumPorts method reports
COM1 <本地端口>
COM2 <本地端口>
COM3 <本地端口>
COM4 <本地端口>
COM5 <本地端口>
COM6 <本地端口>
COM7 <本地端口>
 Time taken: 0 ms

WMI method reports
COM6 <com0com - serial port emulator (COM6)>
COM7 <com0com - serial port emulator (COM7)>
 Time taken: 47 ms

ComDB method reports
CEnumerateSerial::UsingComDB failed, Error:5
 Time taken: 0 ms

Registry method reports
CNCA0
CNCB0
COM1
COM2
COM6
COM7
COM4
 Time taken: 0 ms

UsingGetCommPorts method reports
COM1
COM2
COM6
COM7
COM4
 Time taken: 0 ms