<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Qt實戰(zhàn) | 如何獲取USB設備信息?

          共 6401字,需瀏覽 13分鐘

           ·

          2022-07-31 10:34

              關注、星標公眾號,直達精彩內容

          文章出處:Qt小羅

          整理:李肖遙


          1 需求描述

          1. 實現USB設備的熱插拔狀態(tài)檢測;

          2. 可識別USB設備信息,例如PID、VID、設備序列號等。

          幾年前在CSDN上分享過,被CSDN惡心到了,亂設積分,變相讓用戶充錢,現重新整理代碼進行分享,希望能幫到需要的人。

          2 設計思路

          2.1 獲取USB設備信息的方式?

          經過查詢,Qt可通過本地事件獲取到設備相關信息,用到的的事件函數原型為:


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

          官方說明:這個特殊的事件處理程序可以在子類中重新實現,以接收在消息參數中傳遞的由eventType標識的本機平臺事件。在這個函數的重新實現中,如果您想要停止Qt正在處理的事件,請返回true并設置result,結果參數僅在Windows上有意義。如果返回false,此本機事件將被傳遞回Qt,Qt將該事件轉換為Qt事件并將其發(fā)送給窗口。

          注意:只有當窗口具有本地窗口句柄時,事件才會交付給此事件處理程序。

          很容易理解吧,對于widget窗口而言,這個事件是源頭,Qt封裝好的事件都從這里來,如果直接返回true界面將無法顯示,有興趣的朋友可以試試。另外,發(fā)現沒有這個事件是QWidget的,所以只有窗口界面才有,窗口的句柄可以通過QWidget::winId獲取到,這個后面注冊設備時會用到。

          2.2 準備工作?

          如果只是重寫nativeEvent是不夠的,并不能獲取到設備信息,只能識別熱插拔狀態(tài)。要識別USB設備信息需要用到GUID,先定義設備的GUID,再注冊設備,注冊完成后才能獲取對應設備的本地事件,從而通過事件獲取到設備信息。

          GUID(全稱:Globally Unique Identifier)全局唯一標識符,在windows上使用GUID來管理設備,驅動,總線,類型,塊設備,電源等。

          2.3 常用硬件設備GUID

          IdentifierClass GUID
          GUID_DEVINTERFACE_USB_DEVICE{A5DCBF10-6530-11D2-901F-00C04FB951ED}
          GUID_DEVINTERFACE_USB_HOST_CONTROLLER{A5DCBF10-6530-11D2-901F-00C04FB951ED}
          GUID_DEVINTERFACE_USB_HUB{F18A0E88-C30C-11D0-8815-00A0C906BED8}
          GUID_DEVINTERFACE_NET{CAC88484-7515-4C03-82E6-71A87ABAC361}
          GUID_DEVINTERFACE_DISK{53F56307-B6BF-11D0-94F2-00A0C91EFB8B}
          GUID_DEVINTERFACE_CDROM{53F56308-B6BF-11D0-94F2-00A0C91EFB8B}
          GUID_DEVINTERFACE_KEYBOARD{884B96C3-56EF-11D1-BC8C-00A0C91405DD}
          GUID_DEVINTERFACE_MOUSE{378DE44C-56EF-11D1-BC8C-00A0C91405DD}
          GUID_DEVINTERFACE_IMAGE{6BDD1FC6-810F-11D0-BEC7-08002BE2092F}
          GUID_BTHPORT_DEVICE_INTERFACE{0850302A-B344-4fda-9BE9-90576B8D46F0}
          太多了,網上可以搜到,這里就不一一列出。

          3 代碼實現

          windows提供了接口向系統(tǒng)注冊設備,注冊后當有U盤插拔(或其它volume增刪)的時候,會向注冊的窗口發(fā)送WM_DEVICECHANGE消息:
          HDEVNOTIFY WINAPI RegisterDeviceNotification(
          __in HANDLE hRecipient, // 可以是窗口句柄或者服務句柄
          __in LPVOID NotificationFilter,
          __in DWORD Flags // 制定hRecipient是窗口句柄,還是服務句柄
          );

          1. 先注冊設備,這樣才能接收到消息,代碼如下:

            void Dialog::registerDevice(){    const GUID GUID_DEVINTERFACE_LIST[] = {        { 0xA5DCBF10, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } }, //USB設備的GUID        { 0x53f56307, 0xb6bf, 0x11d0, { 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b } }};
            HDEVNOTIFY hDevNotify; DEV_BROADCAST_DEVICEINTERFACE NotifacationFiler; ZeroMemory(&NotifacationFiler,sizeof(DEV_BROADCAST_DEVICEINTERFACE)); NotifacationFiler.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE); NotifacationFiler.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
            for (int i = 0; i < sizeof(GUID_DEVINTERFACE_LIST)/sizeof(GUID); i++) { NotifacationFiler.dbcc_classguid = GUID_DEVINTERFACE_LIST[i]; hDevNotify = RegisterDeviceNotification((HANDLE)this->winId(), &NotifacationFiler, DEVICE_NOTIFY_WINDOW_HANDLE); if (!hDevNotify) { qCritical() << QStringLiteral("注冊失敗!"); } }}
          2. 通過本地事件獲取消息并解析,代碼如下:

          bool Dialog::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 (WM_DEVICECHANGE == msgType) { PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)msg->lParam; switch (msg->wParam) { case DBT_DEVICEARRIVAL: { if (DBT_DEVTYP_VOLUME == lpdb->dbch_devicetype) { PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb; if (0 == lpdbv->dbcv_flags) { //優(yōu)盤 QString USBDisk = QString(this->firstDriveFromMask(lpdbv ->dbcv_unitmask)); ui->labelShowMsg->setText(QString(QStringLiteral("已檢測到USB設備插入--盤符:<%1>")).arg(USBDisk)); } else if (DBTF_MEDIA == lpdbv->dbcv_flags) { qDebug() << "CD_Arrived."; }
          } else if (DBT_DEVTYP_DEVICEINTERFACE == lpdb->dbch_devicetype) {
          PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)lpdb; QString name = QString::fromWCharArray(pDevInf->dbcc_name); if (!name.isEmpty()) { if (name.contains("USBSTOR")) { QStringList listAll = name.split('#'); QStringList listInfo = listAll.at(1).split('&'); m_usbInfoList.append(listInfo.at(1).mid(4)); //設備制造商 3 m_usbInfoList.append(listInfo.at(2).mid(5)); //設備型號 4 m_usbInfoList.append(listInfo.at(3).mid(4)); //設備版本 5 } else { m_usbInfoList.clear(); QStringList listAll = name.split('#'); QStringList listID = listAll.at(1).split('&'); m_usbInfoList.append(listID.at(0).right(4)); //vid 0 m_usbInfoList.append(listID.at(1).right(4)); //pid 1 m_usbInfoList.append(listAll.at(2)); //設備序列號 2 } }
          display(); } } break; case DBT_DEVICEREMOVECOMPLETE: { if (DBT_DEVTYP_VOLUME == lpdb->dbch_devicetype) { PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb; if (0 == lpdbv->dbcv_flags) { ui->labelShowMsg->setText(QString(QStringLiteral("USB設備已拔出!"))); QTimer::singleShot(1000, ui->labelShowMsg, SLOT(clear())); }
          if (DBTF_MEDIA == lpdbv->dbcv_flags) { ui->labelShowMsg->setText("CD_Removed."); } } } break; } }
          return false;}
          折疊
          補充下,獲取到的USB設備信息原始數據如下,通過上邊的代碼進行解析,然后顯示到界面上。
          "\\\\?\\USB#VID_0951&PID_1666#E0D55EA574B4F371679B1A6D#{a5dcbf10-6530-11d2-901f-00c04fb951ed}""\\\\?\\USBSTOR#Disk&Ven_Kingston&Prod_DataTraveler_3.0&Rev_#E0D55EA574B4F371679B1A6D&0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}"
          發(fā)現沒有,這個GUID和我們注冊的是不是一模一樣,有感覺了吧。如果還想獲取其它設備的相關信息,注冊少不了,不然就獲取不到。再有一個就是,設備信息并不是一個事件就獲取完的,從上可以看出,這里用了兩次事件才將信息獲取完整,即注冊過的兩個設備,可以嘗試將GUID_DEVINTERFACE_LIST列表移除一個GUID,結果是USB設備信息獲取不完整了。

          3 總結

          通過注冊設備、接收本地事件、解析事件消息,最終可獲取到設備信息。如果想進一步實現特殊功能,比如自定義無邊框拉伸、移動等,也可以通過本地事件實現,這需要對windows消息機制進一步學習才行。當然也可以使用Qt封裝好的事件進行處理,都是事件,能玩出什么花樣就得看自己的能耐了。到這里對windows消息機制應該會有個初步的了解,還是那句話,有個好的開端總是很重要的。

          版權聲明:本文來源網絡,免費傳達知識,版權歸原作者所有。如涉及作品版權問題,請聯系我進行刪除。

          ????????????????  END  ???????????????

          關注我的微信公眾號,回復“加群”按規(guī)則加入技術交流群。


          點擊“閱讀原文”查看更多分享,歡迎點分享、收藏、點贊、在看。

          瀏覽 175
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国内精品手机在线视频 | 色婷婷中文在线 | 色老板精品在线 | 天天插天天操天天 | 欧洲操逼视频 |