最近在作一個通用對話框,相似於windows的資源管理器,固然了沒有windwos資源管理器那麼強大。用戶報了一個bug,說通用對話框打開以後不能實時監控U盤插入,隨手在百度上搜索了一圈,這個問題仍是挺多人在搞,都大同小異,基本都是監控windows的事件。下面說下我本身解決該問題的流程。html
WM_DEVICECHANGE:附上WM_DEVICECHANGE消息的連接,此消息意思是當計算機的硬件配置或者設備發生變化時通知應用程序,此消息下在wParam參數中包含了此事件的事件類型。英文:Notifies an application of a change to the hardware configuration of a device or the computer.node
此消息類型下的事件類型有12個,以下表所示c++
|
A request to change the current configuration (dock or undock) has been canceled.windows |
更改當前配置的請求已被取消app |
|
The current configuration has changed, due to a dock or undock.ide |
因爲插入或移除,當前的配置已經改變。函數 |
|
A custom event has occurred.post |
定製事件測試 |
|
A device or piece of media has been inserted and is now available.this |
插入新的設備 |
|
Permission is requested to remove a device or piece of media. Any application can deny this request and cancel the removal. |
請求移除設備,能夠失敗 |
|
A request to remove a device or piece of media has been canceled. |
去除中斷 |
|
A device or piece of media has been removed. |
設備被移除 |
|
A device or piece of media is about to be removed. Cannot be denied. |
即將刪除,仍然有效 |
|
A device-specific event has occurred. |
發生設備特定的事件 |
|
A device has been added to or removed from the system. |
設備被新增或者移除 |
|
Permission is requested to change the current configuration (dock or undock). |
請求權限來更改當前的配置 |
|
The meaning of this message is user-defined. |
此消息的含義是用戶定義的 |
上表所示的消息中咱們用到了其中3個事件,這三個事件分別在合適的時機來獲取優盤事件;初次以外還能夠監控DBT_DEVNODES_CHANGED事件,此事件在優盤插拔時不止一次的響應,若是用戶須要區分插拔事件則此事件不能夠。
DBT_DEVICEARRIVAL://檢測到新設備
DBT_DEVICEREMOVECOMPLETE://設備被移除
DBT_CUSTOMEVENT://右鍵彈出設備時響應,此事件只有用戶進行註冊事後纔會收到
註冊設備狀態發生變化時通知,須要使用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 }
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 }
以下圖1所示,一次完整的優盤插入、彈出和刪除,所觸發的系統事件
圖1 測試截圖
相關鏈接: