(Qt)windows下串口检测-热插拔监测、获取可用串口

更多精彩内容
?个人内容分类汇总 ?

1、主要功能

  • 通过继承QAbstractNativeEventFilter接口实现串口热插拔监测功能;
  • 通过在QWidget中重写nativeEvent实现串口热插拔监测功能;
  • 通过一个函数获取系统中所有可用串口名;
  • 自动添加、移除可用串口。

2、适用系统

3、源码地址

4、实现效果

在这里插入图片描述

5、实现代码

  • comchange.h
/******************************************************************************
 * @文件名       comchange.h
 * @功能      通过继承于QAbstractNativeEventFilter来监测串口热插拔由于windows消息会发给每一个窗口,
 *           所以如果打开多个窗或者使用到如QComboBox之类的控件,当串口插入或者拔出时会触发多次事件,
 *           在这里我使用的解决方法时传入一个窗口句柄,通过窗口句柄过滤事件,但不知道有没有其它更好的办法。
 *
 * @开发者     (作者)
 * @时间       2021/11/15
 * @备注
 *****************************************************************************/
#ifndef COMCHANGE_H
#define COMCHANGE_H

#include <QObject>
#include <qabstractnativeeventfilter.h>
#include <windows.h>
#include "dbt.h"

class ComChange : public QObject, public QAbstractNativeEventFilter
{
    Q_OBJECT
public:
    static ComChange* getInstance();
    static QStringList getAvailablePort();
    void setHWND(HWND hwnd);

private:
    explicit ComChange(QObject *parent = nullptr);

protected:
    bool nativeEventFilter(const QByteArray &eventType, void *message, long *result);

signals:
    /**
     * @brief       传出串口热插拔状态
     * @param name  串口名
     * @param flag  true:插入 false:拔出
     */
    void comStatus(QString name, bool flag);

private:
    static ComChange* m_comChange;
    HWND m_hwnd;

};

#endif // COMCHANGE_H

  • comchange.cpp
#include "comchange.h"
#include <QApplication>
#include <QMutex>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <qdebug.h>


ComChange* ComChange::m_comChange = nullptr;
ComChange *ComChange::getInstance()
{
    if(m_comChange == nullptr)
    {
        static QMutex mutex;         //实例互斥锁。
        QMutexLocker locker(&mutex); //加互斥锁。
        if(m_comChange == nullptr)
        {
            m_comChange = new ComChange();
        }
    }
    return m_comChange;

}

/**
 * @brief   获取系统中所有可用的串口名
 * @return
 */
QStringList ComChange::getAvailablePort()
{
    QStringList strName;
    foreach(const QSerialPortInfo& info, QSerialPortInfo::availablePorts())
    {
        QSerialPort port(info);
        if(port.open(QIODevice::ReadWrite))
        {
            strName << info.portName();
            port.close();
        }
    }
    return strName;
}

/**
 * @brief       设置窗口句柄用于过滤事件
 * @param hwnd
 */
void ComChange::setHWND(HWND hwnd)
{
    this->m_hwnd = hwnd;
}

ComChange::ComChange(QObject *parent) : QObject(parent)
{
    qApp->installNativeEventFilter(this);        // 安装事件过滤器
}

bool ComChange::nativeEventFilter(const QByteArray &eventType, void *message, long *result)
{
    MSG* msg = reinterpret_cast<MSG*>(message);
    if(msg->message == WM_DEVICECHANGE               // 通知应用程序设备或计算机的硬件配置发生更改。
      && msg->hwnd == this->m_hwnd)                  // 过滤事件
    {
        PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)msg->lParam;
        switch (msg->wParam)
        {
        case DBT_DEVICEARRIVAL:             // 插入
        {
            if (lpdb->dbch_devicetype == DBT_DEVTYP_PORT)           // 设备类型为串口
            {
                PDEV_BROADCAST_PORT lpdbv = (PDEV_BROADCAST_PORT)lpdb;
                QString strName = QString::fromWCharArray(lpdbv->dbcp_name);  //插入的串口名
                emit comStatus(strName, true);
            }
            break;
        }
        case DBT_DEVICEREMOVECOMPLETE:      // 拔出
        {
            if (lpdb->dbch_devicetype == DBT_DEVTYP_PORT)           // 设备类型为串口
            {
                PDEV_BROADCAST_PORT lpdbv = (PDEV_BROADCAST_PORT)lpdb;
                QString strName = QString::fromWCharArray(lpdbv->dbcp_name);  //拔出的串口名
                emit comStatus(strName, false);
            }
            break;
        }
        default:
            break;
        }
    }

    return false;
}

  • widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

protected:
    bool nativeEvent(const QByteArray &eventType, void *message, long *result);

    void on_comStatus(QString name, bool flag);

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

  • widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <windows.h>
#include "dbt.h"
#include "comchange.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 第二种方法
    ComChange::getInstance()->setHWND((HWND)this->winId());
    connect(ComChange::getInstance(), &ComChange::comStatus, this, &Widget::on_comStatus);
    ui->comboBox->addItems(ComChange::getAvailablePort());
}

Widget::~Widget()
{
    delete ui;
}

/**
 * @brief               第一种方法
 * @param eventType
 * @param message
 * @param result
 * @return
 */
bool Widget::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
    MSG* msg = reinterpret_cast<MSG*>(message);
    if(msg->message == WM_DEVICECHANGE)                // 通知应用程序设备或计算机的硬件配置发生更改。
    {
        PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)msg->lParam;
        switch (msg->wParam)
        {
        case DBT_DEVICEARRIVAL:             // 插入
        {
            if (lpdb->dbch_devicetype == DBT_DEVTYP_PORT)           // 设备类型为串口
            {
                PDEV_BROADCAST_PORT lpdbv = (PDEV_BROADCAST_PORT)lpdb;
                QString strName = QString::fromWCharArray(lpdbv->dbcp_name);  //插入的串口名
                qDebug() << strName;
            }
            break;
        }
        case DBT_DEVICEREMOVECOMPLETE:      // 拔出
        {
            if (lpdb->dbch_devicetype == DBT_DEVTYP_PORT)           // 设备类型为串口
            {
                PDEV_BROADCAST_PORT lpdbv = (PDEV_BROADCAST_PORT)lpdb;
                QString strName = QString::fromWCharArray(lpdbv->dbcp_name);  //拔出的串口名
                qDebug() << strName;
            }
            break;
        }
        default:
            break;
        }
    }

    return false;
}

void Widget::on_comStatus(QString name, bool flag)
{
    if(flag)
    {
        ui->comboBox->addItem(name);
    }
    else
    {
        ui->comboBox->removeItem(ui->comboBox->findText(name));
    }
}



版权声明:本文为qq_43627907原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。