在Windows操作系統下,所有的事件都是以消息爲驅動的,因此,當我們插U盤和拔U盤時,也是觸發了Windows的消息,我們對此消息進行監聽並接收該消息,就可以探測對U盤的插拔。
01
—
Win32程序
對於消息的監聽,熟悉Windows中Win32程序原理的你,一定非常熟悉,其六大步驟如下:
1. 聲明消息類(WNDCLASS)
2. 註冊消息類(RegisterClass)
3. 創建窗口(CreateWindow)
4. 獲取消息隊列(GetMessage)
5. 消息傳送(TranslateMessage)
6. 消息派發(DispatchMessage)
在聲明消息類WNDCLASS時,設置消息回調wc.lpfnWndProc爲我們自己的函數,即可以接收到系統的消息到該函數中。
以上過程,在main函數中的源碼如下:
int _tmain(int argc, _TCHAR* argv[])
{
isNumber("5.8.");
WNDCLASS wc;
ZeroMemory(&wc, sizeof(wc));
wc.lpszClassName = TEXT("myusbmsg");
wc.lpfnWndProc = WndProc;
RegisterClass(&wc);
HWND h = CreateWindow(TEXT("myusbmsg"), TEXT(""), 0, 0, 0, 0, 0,
0, 0, GetModuleHandle(0), 0);
MSG msg;
while (GetMessage(&msg, 0, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
要接收U盤的插拔消息,就可以在我們設置的回調函數中判斷,假如我們的回調函數聲明如下:
LRESULT CALLBACK WndProc(HWND h, UINT msg, WPARAM wp, LPARAM lp);
首先得判斷收到的消息msg是不是U盤的插拔,因爲Windows的消息非常多,因此得屏蔽掉無關的消息,只處理我們想接受的消息。U盤的插拔所觸發的消息消息爲 WM_DEVICECHANGE 消息,不管是插入U盤,韓式拔掉U盤,都會觸發 WM_DEVICECHANGE 消息。
Windows的消息回調函數還會攜帶WPARAM wp, LPARAM lp連個參數,可以幫助我們判斷U盤是插入還是拔出,以及U盤插入後是
哪個盤符,其詳細解釋如下:
-
wp的值爲 DBT_DEVICEARRIVAL 時,表示U盤插入了。其值爲 DBT_DEVICEREMOVECOMPLETE 時,表示U盤拔出了。
-
將lp進行轉換 DEV_BROADCAST_VOLUME* p = (DEV_BROADCAST_VOLUME*)lp;得到DEV_BROADCAST_VOLUME的指針p,當 p->dbcv_devicetype == DBT_DEVTYP_VOLUME 時,表示卷標發生變化,即可以知道U盤發生了插入或拔出。
-
通過 p->dbcv_unitmask 可以根據其位操作中1的標識位判斷具體是哪個盤發生了插入或拔出,其判斷函數如下:
char FirstDriveFromMask(ULONG unitmask)
{
char i;
for (i = 0; i < 26; ++i)
{
if (unitmask & 0x1)
break;
unitmask = unitmask >> 1;
}
return (i + 'A');
}
通過代碼一目瞭然可知,當p->dbcv_unitmask爲0x1時爲A盤,爲0x10時爲B盤,爲0x100時爲C盤,爲0x1000時爲D盤...以此類推。
以下奉上接收消息的函數,以及U盤插拔判斷的邏輯,如下僅供參考:
LRESULT CALLBACK WndProc(HWND h, UINT msg, WPARAM wp, LPARAM lp)
{
/*
* system uses the WM_DEVICECHANGE message to notify device is changed(USB is inserted or removed).
*/
if (msg == WM_DEVICECHANGE) {
printf("注意---Msg-Change===wp=%d---lp=%d\n", wp, lp);
/*
* #define DBT_DEVNODES_CHANGED 0x0007
* Message = WM_DEVICECHANGE
* wParam = DBT_DEVNODES_CHANGED
* lParam = 0
*
* send when configmg finished a process tree batch. Some devnodes
* may have been added or removed. This is used by ring3 people which
* need to be refreshed whenever any devnode changed occur (like
* device manager). People specific to certain devices should use
* DBT_DEVICE* instead.
*
* The upon windows-msg-param is received when device list is changed, but this time
* system don't know which device is add or remove, after second signal received, the
* below wparam can judge U-Disk or other device.
*/
/*
* The below wparam DBT_DEVICEARRIVAL(system detected a new device) & DBT_DEVICEREMOVECOMPLETE(device is gone)
* can just detect U-Disk inserted or removed(VOLUME List Changed), other device didn't show in volume list
* will not receive DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE(Like Mouse and KeyBorad with not line).
*/
if ((DWORD)wp == DBT_DEVICEARRIVAL) {
DEV_BROADCAST_VOLUME* p = (DEV_BROADCAST_VOLUME*)lp;
if (p->dbcv_devicetype == DBT_DEVTYP_VOLUME) {
char disk = FirstDriveFromMask(p->dbcv_unitmask);
printf("注意---%c盤插進來了\n", disk);
}
}
else if ((DWORD)wp == DBT_DEVICEREMOVECOMPLETE) {
DEV_BROADCAST_VOLUME* p = (DEV_BROADCAST_VOLUME*)lp;
if (p->dbcv_devicetype == DBT_DEVTYP_VOLUME) {
char disk = FirstDriveFromMask(p->dbcv_unitmask);
printf("注意---%c盤被拔掉了\n", disk);
}
}
return TRUE;
}
else return DefWindowProc(h, msg, wp, lp);
}
以上即爲win32接收U盤插拔的監聽探測代碼。
02
—
Qt程序
下面介紹Qt如何監聽探測U盤的插拔消息,由於Windows系統是以消息爲驅動的,因此,想用Qt接受監聽U盤的插拔消息,說到底其實就是Qt如何接收監聽Windows的消息。
在Qt4中,接收監聽Windows消息的Qt函數聲明如下:
bool winEvent(MSG *message, long *result);
在Qt5之後,函數聲明變爲了如下:
bool nativeEvent(const QByteArray &eventType, void *message, long *result);
對以上兩個函數中的message參數進行如下處理:
MSG* msg = reinterpret_cast<MSG*>(message);
即可得到Windows消息的結構體MSG,其定義如下:
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG
因此就可以得到UINT message、WPARAM wParam 以及 LPARAM lParam; 接下來的處理就如同win32中的回調函數WndProc一樣了。
以上不僅僅是Qt對U盤插拔消息的監聽,通過以上Qt的winEvent或nativeEvent函數的重寫,以及相應參數的轉換處理,Qt可以接收監聽所有的Windows的消息,說到這裏,對Win32瞭解的你,一定非常熟悉了。