需要包含的头文件
#include <MessageDispatcherApi.h>
#include <QWidget>
#include <Windows.h>
#include <WinUser.h>
#include <dbt.h>
#include <QApplication>
#include <QDebug>
#include <devguid.h>
#include <SetupAPI.h>
#include <InitGuid.h>
涉及到的类:
tagMSG / MSG
MSG是Windows程序中的结构体,成员结构如下:
typedef struct tagMSG
{
HWND hwnd; // 消息指向的窗口句柄
UINT message; //消息名称
WPARAM wParam; //消息的附加信息
LPARAM lParam; //消息的附加信息
DWORD time; //创建时间
POINT pt; //创建时的鼠标位置
} MSG;
hwnd 消息所属的窗口
message 消息的标识符
例如 WM_DEVICECHANGE(设备拔插变化)、WM_LBUTTONDOWN(鼠标左键按下)、WM_KEYDOWN(键盘按下)等,WM是Windows Message的缩写
wparam 32位整数,表示设备的状态变化
—— DBT_DEVICEARRIVAL 设备已经插入且可以使用了
—— DBT_DEVICEREMOVECOMPLETE 设备已经成功移出
lparam 32位整数,指向DEV—BROADCAST_HDR结构的指针
QT5中使用nativeEvent(const QByteArray &eventType, void *message, long *resutl)代替原来的winEvent函数作消息响应的处理
普通USB的拔插可以通过检测DBT_DEVTYP_PORT以及DBT_DEVNODES_CHANGED的变化来实现,但是我这里需要监测的是单片机上的识别为HID的USB设备,且只有这个特定的设备拔插时才发生变化,故需要对设备进行通知注册
不同类型的设备有特定的唯一的Class GUID,利用这个GUID可以向窗体注册设备通知,我的是HID设备,故应该使用GUID_DEVINTERFACE_HID,使用该GUID要包含“initGUID”头文件
//给设备注册通知
DEV_BROADCAST_DEVICEINTERFACE Notificationfilter;
ZeroMemory(&Notificationfilter,sizeof(DEV_BROADCAST_DEVICEINTERFACE));
Notificationfilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
Notificationfilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
//Notificationfilter.dbcc_classguid ={0x745a17a0,0x74d3,0x11d0,{0xb6,0xfe,0x00,0xa0,0xc9,0x0f,0x57,0xda}};
Notificationfilter.dbcc_classguid = {0x4D1E55B2, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 }};
hDevNotify = RegisterDeviceNotificationW((HANDLE)this->winId(),&Notificationfilter,DEVICE_NOTIFY_WINDOW_HANDLE);
if(!hDevNotify)
{
qDebug() << "注册失败" << endl;
}
注册完毕后检测WM_DEVICECHANGE,进行相应的处理
bool Form::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
Q_UNUSED(eventType);
Q_UNUSED(result);
MSG* msg = reinterpret_cast<MSG*>(message);
int msgType = msg->message;
if(msgType == WM_DEVICECHANGE)
{
PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)msg->lParam;
switch(msg->wParam)
{
case DBT_DEVICEARRIVAL: //有设备插入且可以使用
qDebug() << "DBT_DEVICEARRIVAL";
if(lpdb->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
{
qDebug() << "DBT_DEVTYP_DEVICEINTERFACE";
ui->label->setText(tr("Device is connecting!"));
ui->label->setStyleSheet("font:bold;font-size:20px;color:red;background-color:black");
update = 1;
}
break;
case DBT_DEVICEREMOVECOMPLETE:
if(lpdb->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
{
ui->label->setText(tr("NO Device connecting!"));
ui->label->setStyleSheet("font:bold;font-size:20px;color:white;background-color:black");
update = 0;
}
qDebug() << "DBT_DEVICEREMOVECOMPLETE";
break;
}
}
}
特别地:
当message的类型是WM_DEVICECHANGE时,其lparam是一个指向_DEV_BROADCAST_HDR结构体的指针,结构体内容如下:
typedef struct _DEV_BROADCAST_HDR
{
DWORD dbch_size;
DWORD dbch_devicetype;
DWORD dbch_reserved;
} DEV_BROADCAST_HDR, *PDEV_BROADCAST_HDR;
而当dbch_devicetype是DBT_DEVTYP_VOLUME,即逻辑驱动时,lparam实际指向的是其结构体实例_DEV_BROADCAST_VOLUME,结构体内容如下:
typedef struct _DEV_BROADCAST_VOLUME {
DWORD dbcv_size; //结构体实例的字节数
DWORD dbcv_devicetype; //驱动器类型
DWORD dbcv_reserved;
DWORD dbcv_unitmask; //当前改变的驱动器掩码(驱动器号)
WORD dbcv_flags; //驱动器的类别 1- 光盘驱动 2 - 网路驱动 0 - 硬盘、U盘
} DEV_BROADCAST_VOLUME, *PDEV_BROADCAST_VOLUME;