在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
(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了解的你,一定非常熟悉了。