2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > Qt在Windows上的设备热插拔检测

Qt在Windows上的设备热插拔检测

时间:2023-06-30 14:20:00

相关推荐

Qt在Windows上的设备热插拔检测

一般 Qt 在 Windows 上做设备热插拔需要两个步骤:

1.使用 win32 的 RegisterDeviceNotification 函数注册要监听的设备类别,需要绑定一个窗口 id;

2.重写 QWidget 的nativeEvent 虚函数或者QAbstractNativeEventFilter 的nativeEventFilter 虚函数,处理热插拔相关的回调。

后来参考别人的代码,可以创建一个 win32 的隐藏窗口来接收消息,这样就不用耦合到 Qt 界面上的窗口了。

(-03-21 补充)

如果用通用的 USB GUIDGUID_DEVINTERFACE_USB_DEVICE 来注册,插入 UVC 相机并触发插入消息后,去枚举相机列表,此时可能还枚举不到这个设备,如果注册的 KSCATEGORY_CAPTURE 就能检测到插入的 UVC 相机。

GUID_DEVINTERFACE_USB_DEVICE -- usbiodef.hGUID{ 0xA5DCBF10L, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } }KSCATEGORY_CAPTURE -- Ks.hGUID{ 0x65E8773DL, 0x8F56, 0x11D0, { 0xA3, 0xB9, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96 } }

代码链接:

/gongjianbo/MyTestCode/tree/master/Qt/DeviceHotplug_Win

主要代码:

main.cpp

#include <QApplication>#include <QMainWindow>#include <QDebug>#include "DeviceHotplug.h"#include <Windows.h>#include <Dbt.h>#include <devguid.h>// 具体的设备 GUID 需要 initguid, 如 usbiodef#include <initguid.h>// USB 设备// GUID_DEVINTERFACE_USB_DEVICE#include <usbiodef.h>// HID 人机交互设备-鼠标键盘等#include <hidclass.h>// 键盘 GUID_DEVINTERFACE_KEYBOARD#include <ntddkbd.h>// 鼠标 GUID_DEVINTERFACE_MOUSE#include <ntddmou.h>int main(int argc, char *argv[]){QApplication app(argc, argv);DeviceHotplug filter;QVector<QUuid> uuids;// 监测 USB 插拔uuids << GUID_DEVINTERFACE_USB_DEVICE;// 监测鼠标键盘插拔,有些有扩展功能的鼠标会同时触发键盘和鼠标的插拔事件// uuids << GUID_DEVINTERFACE_KEYBOARD << GUID_DEVINTERFACE_MOUSE;filter.init(uuids);QObject::connect(&filter, &DeviceHotplug::deviceAttached,&app, [](quint16 vid, quint16 pid){qDebug()<<"attached"<<QString::number(vid, 16)<<QString::number(pid, 16);});QObject::connect(&filter, &DeviceHotplug::deviceDetached,&app, [](quint16 vid, quint16 pid){qDebug()<<"detached"<<QString::number(vid, 16)<<QString::number(pid, 16);});QMainWindow w;w.resize(600, 500);w.setWindowTitle("DeviceHotplug (by GongJianBo 1992)");w.show();return app.exec();}

DeviceEventFilter.h

#pragma once#include <QObject>#include <QUuid>#include <QSharedPointer>#include <Windows.h>class DeviceHotplugPrivate;/*** @brief 设备插拔事件监听* @author 龚建波* @date -12-24* @details* 设备类文档,可以在设备管理器找自己的设备 GUID* /zh-cn/windows-hardware/drivers/install/overview-of-device-setup-classes* @history* -03-20* 参考 /wang-bin/qdevicewatcher* 由 QAbstractNativeEventFilter 过滤事件改为了创建一个 win32 窗口来接收事件*/class DeviceHotplug : public QObject{Q_OBJECTpublic:explicit DeviceHotplug(QObject *parent = nullptr);~DeviceHotplug();// RegisterDeviceNotification 注册对应的 GUID 消息通知// 暂未考虑重复注册和注册失败的处理void init(const QVector<QUuid> &uuids);// UnregisterDeviceNotification// 会在析构中自动调用一次void free();signals:// 设备插入void deviceAttached(quint16 vid, quint16 pid);// 设备拔出void deviceDetached(quint16 vid, quint16 pid);private:QSharedPointer<DeviceHotplugPrivate> dptr;};

DeviceEventFilter.cpp

#include "DeviceHotplug.h"#include <QMetaObject>#include <QHash>#include <QDebug>#include <Dbt.h>#pragma comment(lib, "user32.lib")// 内部结构,存储对应平台需要的数据class DeviceHotplugPrivate{public:void deviceAttached(quint16 vid, quint16 pid) {QMetaObject::invokeMethod(ptr, "deviceAttached", Qt::QueuedConnection,Q_ARG(quint16, vid),Q_ARG(quint16, pid));}void deviceDetached(quint16 vid, quint16 pid) {QMetaObject::invokeMethod(ptr, "deviceDetached", Qt::QueuedConnection,Q_ARG(quint16, vid),Q_ARG(quint16, pid));}// 处理窗口消息static LRESULT CALLBACK windowMessageProcess(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);// win32 窗口类名static QString windowClassName();// 创建一个用于接收消息的窗口,注册消息回调bool createMessageWindow(const QVector<QUuid> &uuids);// 释放void destroyMessageWindow();// 关联的对象DeviceHotplug *ptr{nullptr};// 关联的窗口HWND hwnd{nullptr};// 设备通知句柄和 UUID/GUIDQHash<QUuid, HDEVNOTIFY> devNotifys;};// 处理窗口消息LRESULT CALLBACK DeviceHotplugPrivate::windowMessageProcess(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){if (message == WM_DEVICECHANGE) {do {// 设备可用事件const bool is_add = wParam == DBT_DEVICEARRIVAL;// 设备移除事件const bool is_remove = wParam == DBT_DEVICEREMOVECOMPLETE;if (!is_add && !is_remove)break;// 过滤 device interface class 以外类型的消息DEV_BROADCAST_HDR *broadcast = reinterpret_cast<DEV_BROADCAST_HDR *>(lParam);if (!broadcast || broadcast->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)break;// 获取 SetWindowLongPtrW 设置的对象DeviceHotplugPrivate *data = reinterpret_cast<DeviceHotplugPrivate *>(::GetWindowLongPtrW(hwnd, GWLP_USERDATA));if (!data)break;// 过滤不监听的设备类型DEV_BROADCAST_DEVICEINTERFACE *device_interface = reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE *>(broadcast);QUuid uid(device_interface->dbcc_classguid);if (!data->devNotifys.contains(uid))break;QString device_name;if (device_interface->dbcc_name) {#ifdef UNICODEdevice_name = QString::fromWCharArray(device_interface->dbcc_name);#elsedevice_name = QString(device_interface->dbcc_name);#endif}// 从设备描述中获取 vid 和 pidint offset = -1;quint16 vid = 0;quint16 pid = 0;offset = device_name.indexOf("VID_");if (offset > 0 && offset + 8 <= device_name.size()) {vid = device_name.mid(offset + 4, 4).toUShort(nullptr, 16);}offset = device_name.indexOf("PID_");if (offset > 0 && offset + 8 <= device_name.size()) {pid = device_name.mid(offset + 4, 4).toUShort(nullptr, 16);}if (is_add) {fprintf(stderr, "device attached: vid 0x%04x, pid 0x%04x.\n", vid, pid);data->deviceAttached(vid, pid);} else if (is_remove) {fprintf(stderr, "device detached: vid 0x%04x, pid 0x%04x.\n", vid, pid);data->deviceDetached(vid, pid);}} while(false);}return ::DefWindowProcW(hwnd, message, wParam, lParam);}// 窗口类名QString DeviceHotplugPrivate::windowClassName(){return QLatin1String("Qt_DeviceHotplug_Window_") + QString::number(quintptr(windowMessageProcess));}// 创建一个用于接收消息的窗口,注册消息回调bool DeviceHotplugPrivate::createMessageWindow(const QVector<QUuid> &uuids){QString class_name = windowClassName();HINSTANCE hi = ::GetModuleHandleW(nullptr);WNDCLASSW wc;memset(&wc, 0, sizeof(WNDCLASSW));wc.lpfnWndProc = windowMessageProcess;wc.cbClsExtra = 0;wc.cbWndExtra = 0;wc.hInstance = hi;wc.lpszClassName = reinterpret_cast<const wchar_t *>(class_name.utf16());::RegisterClassW(&wc);hwnd = ::CreateWindowW(wc.lpszClassName, // classnamewc.lpszClassName, // window name0, // style0, // x0, // y0, // width0, // height0, // parent0, // menu handlehi, // application0); // windows creation data.if (!hwnd) {qDebug()<<"createMessageWindow error"<<(int)GetLastError();} else {// 初始化 DEV_BROADCAST_DEVICEINTERFACE 数据结构DEV_BROADCAST_DEVICEINTERFACE_W filter_data;memset(&filter_data, 0, sizeof(DEV_BROADCAST_DEVICEINTERFACE_W));filter_data.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE_W);filter_data.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;for (auto &&uuid : uuids){filter_data.dbcc_classguid = uuid;HDEVNOTIFY handle = ::RegisterDeviceNotificationW(hwnd, &filter_data, DEVICE_NOTIFY_WINDOW_HANDLE);if (handle) {devNotifys.insert(uuid, handle);} else {qDebug()<<"RegisterDeviceNotification error"<<uuid;}}::SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)this);}return !!hwnd;}// 释放void DeviceHotplugPrivate::destroyMessageWindow(){if (hwnd) {::DestroyWindow(hwnd);hwnd = nullptr;for (HDEVNOTIFY handle : qAsConst(devNotifys)){::UnregisterDeviceNotification(handle);}devNotifys.clear();}::UnregisterClassW(reinterpret_cast<const wchar_t *>(windowClassName().utf16()), ::GetModuleHandleW(nullptr));}DeviceHotplug::DeviceHotplug(QObject *parent): QObject{parent}, dptr{new DeviceHotplugPrivate}{dptr->ptr = this;}DeviceHotplug::~DeviceHotplug(){free();}void DeviceHotplug::init(const QVector<QUuid> &uuids){const bool ret = dptr->createMessageWindow( uuids);if (!ret) {dptr->destroyMessageWindow();}}void DeviceHotplug::free(){dptr->destroyMessageWindow();}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。