Qt篇——Qt使用C++获取Windows电脑上所有外接设备的名称、物理端口位置等信息

        我之前有发过一篇文章《Qt篇——获取Windows系统上插入的串口设备的物理序号》,文章中主要获取的是插入的USB串口设备的物理序号;而本篇文章则进行拓展,可以获取所有外接设备的相关信息(比如USB摄像头、USB蓝牙、USB网卡、其它一些可拔插的设备等等),并且代码进行了优化、精简。代码如下,可直接cv享用。

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

#pragma comment(lib, "setupapi.lib")

void DevicesTool::listCameras() {
    // 获取所有图像设备, GUID_DEVCLASS_USB、GUID_DEVCLASS_SYSTEM等等都可以,根据实际需求选择
    HDEVINFO deviceInfoSet = SetupDiGetClassDevs(&GUID_DEVCLASS_USB, nullptr, nullptr, DIGCF_PRESENT | DIGCF_ALLCLASSES);
    if (deviceInfoSet == INVALID_HANDLE_VALUE) {
        qDebug() << "Failed to get device info set";
        return;
    }

    SP_DEVINFO_DATA deviceInfoData;
    deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);

    // 遍历设备列表
    for (DWORD i = 0; SetupDiEnumDeviceInfo(deviceInfoSet, i, &deviceInfoData); i++) {
        TCHAR deviceName[256];
        QString deviceDescStr = "", deviceNameStr = "", deviceLocationInfoStr = "", deviceLocationPathStr;
        if (SetupDiGetDeviceRegistryProperty(deviceInfoSet, &deviceInfoData, SPDRP_DEVICEDESC, nullptr,
                                             (PBYTE)deviceName, sizeof(deviceName), nullptr)) {
            deviceDescStr= QString::fromWCharArray(deviceName);
        }
        if (SetupDiGetDeviceRegistryProperty(deviceInfoSet, &deviceInfoData, SPDRP_FRIENDLYNAME, nullptr,
                                             (PBYTE)deviceName, sizeof(deviceName), nullptr)) {
            deviceNameStr = QString::fromWCharArray(deviceName);
        }
        if (SetupDiGetDeviceRegistryProperty(deviceInfoSet, &deviceInfoData, SPDRP_LOCATION_INFORMATION, nullptr,
                                             (PBYTE)deviceName, sizeof(deviceName), nullptr)) {
            deviceLocationInfoStr = QString::fromWCharArray(deviceName);
        }
        if (SetupDiGetDeviceRegistryProperty(deviceInfoSet, &deviceInfoData, SPDRP_LOCATION_PATHS, nullptr,
                                             (PBYTE)deviceName, sizeof(deviceName), nullptr)) {
            deviceLocationPathStr = QString::fromWCharArray(deviceName);
        }
        if (deviceLocationInfoStr != "" && deviceDescStr != "" && deviceNameStr != "" && deviceLocationPathStr != "") {
            qDebug() << "Device: " << deviceDescStr << deviceNameStr << deviceLocationInfoStr << deviceLocationPathStr;
        }
        //可以再筛选一条属性
/**
        int index = -1;
        if (SetupDiGetDeviceRegistryProperty(deviceInfoSet, &deviceInfoData, SPDRP_DRIVER, nullptr,
                                             (PBYTE)deviceName, sizeof(deviceName), nullptr)) {
            index = QString::fromWCharArray(deviceName).split("\\")[1].toInt();
        }
        if (deviceLocationInfoStr != "" && deviceDescStr != ""
            && deviceNameStr != "" && deviceLocationPathStr != "" && index != -1) {
            qDebug() << "Device: " << deviceDescStr << deviceNameStr << deviceLocationInfoStr << deviceLocationPathStr << index;
            cameraList.append(new DeviceInfo(deviceNameStr, deviceDescStr, deviceLocationInfoStr, deviceLocationPathStr, index));
        }
*/
    }

    SetupDiDestroyDeviceInfoList(deviceInfoSet);
}

打印结果如下:

 比如我获取到的4个USB 视频设备就是我插入的3个USB相机、系统自带的相机,这里就可以知道哪个相机插在哪个USB口上面了(右键此电脑-管理-设备管理器-照相机,双击我要查看的相机,里面的“位置”就是上面代码中获取到的deviceLocationInfoStr)。

        ps:其实还有更多设备信息可以查看,只是我打印的地方进行了筛选,只有4项信息(物理端口信息、物理端口路径、设备信息、设备名称)都能够获取的才进行打印,可以根据你实际需求降低筛选。 

需要了解代码原理的看这里,这里简单地介绍各个函数的用途:

1.SetupDiGetClassDevs函数返回一个包含本机上所有被请求的设备信息的设备信息集合句柄。

HDEVINFO SetupDiGetClassDevs(
  _In_opt_ const GUID   *ClassGuid, // 一个指向GUID的指针,此GUID可标识一个设备安装类或一个设备接口类, 可以为NULL
  _In_opt_       PCTSTR Enumerator, // 一个指向以空字符结束的字符串的指针
  _In_opt_       HWND   hwndParent, // 用于与在设备信息集中安装设备实例相关联的用户界面的顶级窗口句柄
  _In_           DWORD  Flags       // 通过此参数来过滤指定的设备信息集中的设备, DIGCF_PRESENT表示只返回当前系统中存在的(已连接)设备。
);

        该函数的第一个入口参数GUID指定了我们想要检索什么类型的设备,一些常用的GUID如下:

GUID_DEVCLASS_SYSTEM    // 系统设备GUID
GUID_DEVCLASS_USB       // USB设备GUID
GUID_DEVCLASS_MOUSE     // 鼠标设备GUID
GUID_DEVCLASS_NET       // 网络设备GUID
GUID_DEVCLASS_KEYBOARD  // 键盘设备GUID

        当调用完此函数并处理完相应数据后,必须调用SetupDiDestroyDeviceInfoList函数,否则内存溢出。

2.SetupDiEnumDeviceInfo函数返回一个SP_DEVINFO_DATA结构,它指定该设备的信息集的设备的信息元素。

BOOL SetupDiEnumDeviceInfo(
    _In_ HDEVINFO DeviceInfoSet,          // 设备信息集的句柄,即SetupDiGetClassDevs返回的句柄
    _In_ DWORD MemberIndex,               // 要检索的设备信息元素的从零开始的索引
    _Out_ PSP_DEVINFO_DATA DeviceInfoData // 指向SP_DEVINFO_DATA结构的指针,以接收有关枚举设备信息元素的信息
    );

3.SetupDiGetDeviceRegistryProperty检索指定的即插即用设备属性.

BOOL SetupDiGetDeviceRegistryPropertyW(
    _In_ HDEVINFO DeviceInfoSet,          // 设备信息集的句柄,即SetupDiGetClassDevs返回的句柄
    _In_ PSP_DEVINFO_DATA DeviceInfoData, // 指向SP_DEVINFO_DATA结构的指针,该结构指定DeviceInfoSet中的设备信息元素
    _In_ DWORD Property,                  // 指定要检索的属性
    _Out_opt_ PDWORD PropertyRegDataType, // 指向一个变量的指针,该变量接收要检索的属性的数据类型。
    _Out_writes_bytes_to_opt_(PropertyBufferSize, *RequiredSize) PBYTE PropertyBuffer, // 指向缓冲区的指针,该缓冲区接收正在检索的属性
    _In_ DWORD PropertyBufferSize,        // PropertyBuffer缓冲区的大小(单位:字节)
    _Out_opt_ PDWORD RequiredSize         // 指向DWORD类型的变量的指针,该变量接收所需的PropertyBuffer缓冲区的大小(单位:字节)
    );

        该函数的第三个入口参数Property决定了我们想要检索设备的什么属性,它的取值可以在SetupAPI.h文件里查找,一些常用的属性的含义如下:

#常用
#define SPDRP_DEVICEDESC                  (0x00000000)  // DeviceDesc (R/W)
#define SPDRP_FRIENDLYNAME                (0x0000000C)  // FriendlyName (R/W)
#define SPDRP_LOCATION_INFORMATION        (0x0000000D)  // LocationInformation (R/W)

#其他
#define SPDRP_HARDWAREID                  (0x00000001)  // HardwareID (R/W)
#define SPDRP_COMPATIBLEIDS               (0x00000002)  // CompatibleIDs (R/W)
#define SPDRP_CLASS                       (0x00000007)  // Class (R--tied to ClassGUID)
#define SPDRP_CLASSGUID                   (0x00000008)  // ClassGUID (R/W)
 

### 解决串口调试助手无法检测到串口的方法 #### 硬件连接验证 确保串口设备已经正确连接到计算机的串口端口,并且设备电源处于开启状态。如果使用的是USB转串口适配器,则需进一步确认该适配器本身无物理损坏并稳固插入电脑USB接口[^1]。 #### 驱动程序检查 核实串口设备对应的驱动程序是否成功安装,在Windows操作系统下可通过打开“设备管理器”,找到相应的串行通信控制器节点来判断;对于Linux系统而言,可利用`dmesg | grep ttyS`命令查看内核消息中有关TTY设备信息以确认驱动加载情况。若发现异常提示或黄色感叹号标记,表明可能存在驱动缺失或不兼容的情况,此时应前往制造商官网下载最新版本驱动进行更新操作。 #### 软件配置审查 进入所使用的串口调试软件界面(例如SuperCom),仔细核查当前设定参数——包括但不限于波特率、数据位数、奇偶校验模式及停止位长度等选项,务必使这些数值同实际硬件需求保持一致。错误的配置可能导致即便物理层面上一切正常也无法实现有效通讯。 #### 设备重启尝试 分别对主机与外接串口装置执行一次完整的开关机流程,以此促使内部缓存清空以及各项初始化过程得以重新运行,从而提高识别成功的几率。有时简单的重启动作便能解决不少棘手的小故障。 ```cpp // 检测可用串口示例代码 (适用于Qt框架下的C++应用程序) QList<QSerialPortInfo> serialList = QSerialPortInfo::availablePorts(); if (!serialList.isEmpty()) { foreach(const QSerialPortInfo& info, serialList){ qDebug()<<info.portName()<<" : "<<info.description(); } } else{ qDebug()<<"No available ports."; } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

易Yi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值