无法使用winAPI工具检测Windows 10下可用的串行端口

时间:2017-09-22 13:41:21

标签: c++ winapi serial-port

我的程序通常会在启动时检测操作系统中可用的串行端口。如果可以按名称访问端口,则这是一种简单的轮询方法。

项目定义了串口

std::string COMPortNumber[MAXPORTS] {"\\\\.\\COM1",  "\\\\.\\COM2",  "\\\\.\\COM3",  "\\\\.\\COM4",  "\\\\.\\COM5",
                                 "\\\\.\\COM6",  "\\\\.\\COM7",  "\\\\.\\COM8",  "\\\\.\\COM9",  "\\\\.\\COM10",
                                 "\\\\.\\COM11", "\\\\.\\COM12", "\\\\.\\COM13", "\\\\.\\COM14", "\\\\.\\COM15",
                                 "\\\\.\\COM16", "\\\\.\\COM17", "\\\\.\\COM18", "\\\\.\\COM19", "\\\\.\\COM20"};

std::string COMPortName[MAXPORTS] = {"com1",  "com2",  "com3",  "com4",  "com5",  "com6",  "com7",  "com8",  "com9", "com10",
                                "com11", "com12", "com13", "com14", "com15", "com16", "com17", "com18", "com19", "com20"};

轮询功能:

void updateSerialList(){
ComboBox_ResetContent(SerialPortDropDown); //clears all content from drop down box
//int iresult = ComboBox_AddString(SerialPortDropDown, "Update Port List\0");
for(int n=0; n<MAXPORTS; n++)
{
    COMPortAvailable[n] = serial.getComPortList( COMPortNumber[n] );
    if(COMPortAvailable[n] == true)
    {
        char* tempBuf = new char[COMPortName[n].length() + 1];
        for(unsigned int t=0; t<COMPortName[n].length(); t++)
        {
            tempBuf[t] = COMPortName[n][t];
        }
        tempBuf[COMPortName[n].length()] = '\0';
        int iResult = ComboBox_AddString(SerialPortDropDown, tempBuf);
        {
            if(iResult == CB_ERR){std::cout << "error adding string" << std::endl;}
            else if(iResult == CB_ERRSPACE){std::cout << "error no room" << std::endl;}
        }
        delete[] tempBuf;
    }
}
//place baud rates in select box
for(int n=NUMBERBAUDRATES-1; n>-1; n--)
{
    char* tempBuf = new char[BaudRateName[n].length() + 1];
    for(unsigned int t=0; t<BaudRateName[n].length(); t++)
    {
        tempBuf[t] = BaudRateName[n][t];
    }
    tempBuf[BaudRateName[n].length()] = '\0';
    int iResult = ComboBox_AddString(BaudRateDropDown, tempBuf);
    {
        if(iResult == CB_ERR){std::cout << "error adding string" << std::endl;}
        else if(iResult == CB_ERRSPACE){std::cout << "error no room" << std::endl;}
    }
    delete[] tempBuf;
}

这会在下拉框中编译一个列表供用户选择。它在类中使用函数作为串行实例。这是类中的函数调用。

bool getComPortList(std::string portName)
{
    bool test;
    HANDLE testSerial;
    testSerial = CreateFile(   (portName.c_str())  , GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, NULL, NULL);
    if(testSerial == INVALID_HANDLE_VALUE)
    {
        test = false;
    }
    else
    {
        test = true;
        cout << "port number " << portName << " is available" << endl;
    }
    CloseHandle(testSerial);
    return test;
}

这种方法运行良好,直到我尝试在Windows 10上运行该程序。它之前已在Vista,Win7,Win 8.1上进行过测试和使用,但即使Windows10设备管理器说系统上有通讯端口,我的程序也不能得到他们的清单。

Win10串口访问有何不同?

1 个答案:

答案 0 :(得分:2)

您的主要逻辑错误,您认为 - 如果某些名称的<{1}} 返回CreateFile - 这意味着此名称不存在。但这当然是错误的,因为INVALID_HANDLE_VALUE可能由于不同的原因而失败。失败后需要致电CreateFile。只有当它返回GetLastError时,名称才真正存在(ERROR_FILE_NOT_FOUND不能用于ERROR_PATH_NOT_FOUND,因为此处的路径始终存在且正确)。对于com设备非常常见的错误是"\\\\.\\COMX" - 因为它有STATUS_ACCESS_DENIED标志。用这个标志only one file on device can be open at a time

但是对于枚举com设备 - 您需要通过DO_EXCLUSIVE枚举GUID_DEVINTERFACE_COMPORT的接口

CM_Get_Device_Interface_ListW

和演示输出:

enumInterfaces(const_cast<PGUID>(&GUID_DEVINTERFACE_COMPORT));

static volatile UCHAR guz;

void enumInterfaces(PGUID InterfaceClassGuid)
{
    CONFIGRET status;
    ULONG len = 0, cb = 0, rcb;
    PVOID stack = alloca(guz);
    PWSTR buf = 0;

    do 
    {
        if (status = CM_Get_Device_Interface_List_SizeW(&len, InterfaceClassGuid, 0, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
        {
            break;
        }

        if (cb < (rcb = len * sizeof(WCHAR)))
        {
            len = (cb = RtlPointerToOffset(buf = (PWSTR)alloca(rcb - cb), stack)) / sizeof(WCHAR);
        }

        status = CM_Get_Device_Interface_ListW(InterfaceClassGuid, 0, buf, len, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);

        if (status == CR_SUCCESS)
        {
            while (*buf)
            {
                DbgPrint("use this name in CreateFile = %S\n", buf);

                PrintFriendlyNameByInterface(buf);

                buf += 1 + wcslen(buf);
            }
        }

    } while (status == CR_BUFFER_SMALL);
}

CONFIGRET PrintFriendlyNameByInterface(PCWSTR pszDeviceInterface)
{
    ULONG cb = 0, rcb = 64;

    PVOID stack = alloca(guz);
    DEVPROPTYPE PropertyType;

    CONFIGRET status;

    union {
        PVOID pv;
        PWSTR DeviceID;
        PBYTE pb;
    };

    do 
    {
        if (cb < rcb)
        {
            rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
        }

        status = CM_Get_Device_Interface_PropertyW(pszDeviceInterface, &DEVPKEY_Device_InstanceId, &PropertyType, pb, &rcb, 0);

        if (status == CR_SUCCESS)
        {
            if (PropertyType == DEVPROP_TYPE_STRING)
            {
                DbgPrint("DeviceID = %S\n", DeviceID);

                status = PrintFriendlyNameByDeviceID(DeviceID);
            }
            else
            {
                status = CR_WRONG_TYPE;
            }

            break;
        }

    } while (status == CR_BUFFER_SMALL);

    return status;
}

CONFIGRET PrintFriendlyNameByDeviceID(PWSTR DeviceID)
{
    DEVINST dnDevInst;

    CONFIGRET status = CM_Locate_DevNodeW(&dnDevInst, DeviceID, CM_LOCATE_DEVNODE_NORMAL);

    if (status == CR_SUCCESS)
    {
        ULONG cb = 0, rcb = 256;

        PVOID stack = alloca(guz);

        DEVPROPTYPE PropertyType;

        union {
            PVOID pv;
            PWSTR sz;
            PBYTE pb;
        };

        do 
        {
            if (cb < rcb)
            {
                rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
            }

            status = CM_Get_DevNode_PropertyW(dnDevInst, &DEVPKEY_NAME, &PropertyType, pb, &rcb, 0);

            if (status == CR_SUCCESS)
            {
                if (PropertyType == DEVPROP_TYPE_STRING)
                {
                    DbgPrint("show this name for user = %S\n", sz);
                }
                else
                {
                    status = CR_WRONG_TYPE;
                }
            }

        } while (status == CR_BUFFER_SMALL);
    }

    return status;
}
我的系统中的

use this name in CreateFile = \\?\ACPI#PNP0501#0#{86e0d1e0-8089-11d0-9ce4-08003e301f73} DeviceID = ACPI\PNP0501\0 show this name for user = Communications Port (COM1) PDO 设备\\?\ACPI#PNP0501#0#{86e0d1e0-8089-11d0-9ce4-08003e301f73}的符号链接(由 aspi.sys 创建)并且它没有{{1标志。尽管在第二次调用CreateFile时我得到了访问被拒绝错误。附加到此设备 FDO - \Device\00000034DO_EXCLUSIVE符号链接)。它已经有\Device\Serial0标志。无论如何\\?\COM1DO_EXCLUSIVE procedure serial.sys )拒绝访问会创建多个文件 - 在设备扩展中一些计数器开始递增,如果它!= 1 - 返回SerialCreateOpen

所以即使我们尝试打开 PDO IRP_MJ_CREATE)而不是独占设备(此处为setting the exclusive flag for the FDO has no effect) - 创建请求开始在{{1}的堆栈顶部执行}和 serial.sys enforce exclusivity themselves within their STATUS_ACCESS_DENIED例程。

enter image description here