Qt之移動硬盤熱插拔監控

    最近在作一個通用對話框,相似於windows的資源管理器,固然了沒有windwos資源管理器那麼強大。用戶報了一個bug,說通用對話框打開以後不能實時監控U盤插入,隨手在百度上搜索了一圈,這個問題仍是挺多人在搞,都大同小異,基本都是監控windows的事件。下面說下我本身解決該問題的流程。html

1、windows消息

WM_DEVICECHANGE:附上WM_DEVICECHANGE消息的連接,此消息意思是當計算機的硬件配置或者設備發生變化時通知應用程序,此消息下在wParam參數中包含了此事件的事件類型。英文:Notifies an application of a change to the hardware configuration of a device or the computer.node

此消息類型下的事件類型有12個,以下表所示c++

DBT_CONFIGCHANGECANCELED
0x0019

A request to change the current configuration (dock or undock) has been canceled.windows

更改當前配置的請求已被取消app

DBT_CONFIGCHANGED
0x0018

The current configuration has changed, due to a dock or undock.ide

因爲插入或移除,當前的配置已經改變。函數

DBT_CUSTOMEVENT
0x8006

A custom event has occurred.post

定製事件測試

DBT_DEVICEARRIVAL
0x8000

A device or piece of media has been inserted and is now available.this

插入新的設備

DBT_DEVICEQUERYREMOVE
0x8001

Permission is requested to remove a device or piece of media. Any application can deny this request and cancel the removal.

請求移除設備,能夠失敗

DBT_DEVICEQUERYREMOVEFAILED
0x8002

A request to remove a device or piece of media has been canceled.

去除中斷

DBT_DEVICEREMOVECOMPLETE
0x8004

A device or piece of media has been removed.

設備被移除

DBT_DEVICEREMOVEPENDING
0x8003

A device or piece of media is about to be removed. Cannot be denied.

即將刪除,仍然有效

DBT_DEVICETYPESPECIFIC
0x8005

A device-specific event has occurred.

發生設備特定的事件

DBT_DEVNODES_CHANGED
0x0007

A device has been added to or removed from the system.

設備被新增或者移除

DBT_QUERYCHANGECONFIG
0x0017

Permission is requested to change the current configuration (dock or undock).

請求權限來更改當前的配置

DBT_USERDEFINED
0xFFFF

The meaning of this message is user-defined.

此消息的含義是用戶定義的

    上表所示的消息中咱們用到了其中3個事件,這三個事件分別在合適的時機來獲取優盤事件;初次以外還能夠監控DBT_DEVNODES_CHANGED事件,此事件在優盤插拔時不止一次的響應,若是用戶須要區分插拔事件則此事件不能夠。

DBT_DEVICEARRIVAL://檢測到新設備

DBT_DEVICEREMOVECOMPLETE://設備被移除

DBT_CUSTOMEVENT://右鍵彈出設備時響應,此事件只有用戶進行註冊事後纔會收到

2、註冊設備通知

    註冊設備狀態發生變化時通知,須要使用RegisterDeviceNotification接口,註冊方法代碼以下,關鍵註釋都有,就不解釋了。

 1 bool diskoperate::registerDisk(const QString & cDiskName)
 2 {
 3     if (!IsDiskExist(cDiskName.at(0).toLatin1()))
 4     {
 5         return false;
 6     }
 7 
 8     const UINT oldmode = ::SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);//不彈出系統提示
 9     HANDLE  handle = CreateFile(
10         cDiskName.toStdWString().c_str() ,
11         GENERIC_READ,
12         FILE_SHARE_READ | FILE_SHARE_WRITE,
13         0,
14         OPEN_EXISTING,
15         FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL,
16         0);//拿到盤符句柄
17     if (handle == nullptr)
18     {
19         return false;
20     }
21     DEV_BROADCAST_HANDLE  NotificationFilter;
22     ZeroMemory( &NotificationFilter, sizeof (NotificationFilter) );
23     NotificationFilter.dbch_size = sizeof (DEV_BROADCAST_HANDLE );
24     NotificationFilter.dbch_devicetype = DBT_DEVTYP_HANDLE;
25     NotificationFilter.dbch_handle = handle;
26     HDEVNOTIFY  hDevNotify = RegisterDeviceNotification((HWND)this->winId()
27         , &NotificationFilter
28         , DEVICE_NOTIFY_WINDOW_HANDLE);//註冊設備通知
29     
30     CloseHandle(handle);//關閉盤符句柄
31     ::SetErrorMode(oldmode);//恢復以前錯誤模式
32     if (!hDevNotify)
33     {
34         return false;
35     }
36 
37     m_lstMoveDrive[cDiskName.at(0)] = hDevNotify;
38 
39     return true;
40 }

上述方法中用了一個判斷當前盤符是否存在的函數IsDiskExist,此函數也比較簡單,用過GetLogicalDrives接口獲取當前系統已有盤符,進行比較便可。

 1 bool IsDiskExist(char cDiskName)  
 2 {  
 3     DWORD dwDrivers;  
 4     int i = toupper(cDiskName) - 'A';  
 5 
 6     //dwDrivers的每個二進制位表示對應的驅動器是否存在。  
 7     dwDrivers = GetLogicalDrives();  
 8     //判斷當前位是否有驅動器  
 9     if ((dwDrivers & (1 << (i))) != 0)  
10     {  
11         return true;  
12     }  
13     return false;  
14 }  

3、獲取系統事件

    Qt給咱們提供了一個QAbstractNativeEventFilter類,該類能夠過濾應用程序的全部事件,所以咱們的類須要繼承並實現此類中的nativeEventFilter方法,在此方法中進行事件過濾,以前寫過一個相關的文章qt捕獲全局windows消息能夠進行參考下,,nativeEventFilter方法想要進行事件過濾,咱們必須使用qApp->installNativeEventFilter(this);方法進行註冊咱們本身寫的類。

 1 bool diskoperate::nativeEventFilter( const QByteArray &eventType, void *message, long *result )
 2 {
 3     if ("windows_dispatcher_MSG" == eventType
 4         || "windows_generic_MSG" == eventType)
 5     {
 6         MSG * msg = reinterpret_cast<MSG *>(message);
 7         if(msg->message == WM_DEVICECHANGE)
 8         {
 9             PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)msg->lParam;
10             switch(msg->wParam)
11             {
12             case DBT_DEVICEARRIVAL://檢測到新設備
13                 if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME)
14                 {
15                     qDebug() << "DBT_DEVICEARRIVAL";
16                     updateMoveDrives();
17                 }
18                 break;
19             case DBT_DEVICEQUERYREMOVE://請求移除設備,可能失敗  此時刷新不會讓移動設備消失
20                 if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME)
21                 {
22                     qDebug() << "DBT_DEVICEQUERYREMOVE";
23                 }
24                 break;
25             case DBT_DEVICEQUERYREMOVEFAILED://去除中斷
26                 if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME)
27                 {
28                     qDebug() << "DBT_DEVICEQUERYREMOVEFAILED";
29                 }
30                 break;
31             case DBT_DEVICEREMOVEPENDING://即將刪除,仍然有效
32                 if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME)
33                 {
34                     qDebug() << "DBT_DEVICEREMOVEPENDING";
35                 }
36                 break;
37             case DBT_DEVICEREMOVECOMPLETE://設備不見了
38                 if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME)
39                 {
40                     qDebug() << "DBT_DEVICEREMOVECOMPLETE";
41                     updateMoveDrives();
42                 }
43                 break;
44             case DBT_CUSTOMEVENT:
45                 if (lpdb->dbch_devicetype == DBT_DEVTYP_HANDLE)
46                 {
47                     qDebug() << "DBT_CUSTOMEVENT";
48                     updateMoveDrives();
49                 }
50                 break;
51             case DBT_DEVNODES_CHANGED:
52                 qDebug() << "DBT_DEVNODES_CHANGED";
53                 updateMoveDrives();
54                 break;
55             default:
56                 qDebug() << msg->wParam;
57             }
58             outputDrives();
59         }
60     }
61 
62     return __super::nativeEvent(eventType, message, result);
63 }
View Code

4、運行效果

     以下圖1所示,一次完整的優盤插入、彈出和刪除,所觸發的系統事件

圖1 測試截圖

 

5、下載鏈接

  Qt之USB熱插拔源碼下載實例

 

相關鏈接:

一、想用c++寫一個監測在win7下的usb插拔監測

二、接收不到DBT_DEVICEQUERYREMOVE消息怎麼辦?

三、qt捕獲全局windows消息

四、USB設備註冊與插拔監聽

五、mfc檢測usb插拔事件

相關文章
相關標籤/搜索