windows用户模式下检测即插即用设备的变化
序:
在21世纪,这个信息时代,热插拔设备是一个巨大的安全隐患。在这个篇文章中,我将介绍一种在用户模式下检测即插即用设备的方法。比如,在系统中插入一个usb设备,ipod,无线网卡等等,都可以在用户模式下检测到,并决定开启或关闭新插入的设备。并且,在文章结尾,我将介绍一下这种方法的优点,以及限制。
怎样检测硬件改变呢?
事实上,windows操作系统在检测到硬件变化时,会发送一个WM_DEVICECHANGE硬件change消息。因此,我们要做的就是在我们的程序中添加WM_DEVICECHANGE的消息响应。
代码事例如下:
BEGIN_MESSAGE_MAP(CHWDetectDlg, CDialog)zai // ... other handlers ON_MESSAGE(WM_DEVICECHANGE, OnMyDeviceChange)END_MESSAGE_MAP()LRESULT CHWDetectDlg::OnMyDeviceChange(WPARAM wParam, LPARAM lParam){ // for more information, see MSDN help of WM_DEVICECHANGE // this part should not be very difficult to understand if ( DBT_DEVICEARRIVAL == wParam || DBT_DEVICEREMOVECOMPLETE == wParam ) { PDEV_BROADCAST_HDR pHdr = (PDEV_BROADCAST_HDR)lParam; switch( pHdr->dbch_devicetype ) { case DBT_DEVTYP_DEVICEINTERFACE: PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr; // do something... break; case DBT_DEVTYP_HANDLE: PDEV_BROADCAST_HANDLE pDevHnd = (PDEV_BROADCAST_HANDLE)pHdr; // do something... break; case DBT_DEVTYP_OEM: PDEV_BROADCAST_OEM pDevOem = (PDEV_BROADCAST_OEM)pHdr; // do something... break; case DBT_DEVTYP_PORT: PDEV_BROADCAST_PORT pDevPort = (PDEV_BROADCAST_PORT)pHdr; // do something... break; case DBT_DEVTYP_VOLUME: PDEV_BROADCAST_VOLUME pDevVolume = (PDEV_BROADCAST_VOLUME)pHdr; // do something... break; } } return 0;}
然而,在默认情况下,windows操作系统只是发送WM_DEVICECHANGE消息给同时具备以下条件的程序(以下是原文):
1.All applications with a top-level window, and
2.Only upon port and volume change.
这样的设计其实并不坏,至少操作系统给了你提示,在你的应用程序中检测设备加载与卸载的提示。你可以在设备变更后通过DEV_BROADCAST_VOLUME.dbcv_unitmask捕捉设备消息。缺点是当你获得提示时,你的系统已经加载或卸载了设备。
RegisterDeviceNotification()
想在其他类型的设备改变时获得通知,或者当你的程序不是top-level窗口时,你可以通过调用RegisterDeviceNotification() API来实现。
比如,当你想要获得upon interface改变的通知时,你可以这样做:
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;ZeroMemory( &NotificationFilter, sizeof(NotificationFilter) );NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;// assume we want to be notified with USBSTOR// to get notified with all interface on XP or above// ORed 3rd param with DEVICE_NOTIFY_ALL_INTERFACE_CLASSES and dbcc_classguid will be ignoredNotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_USBSTOR;HDEVNOTIFY hDevNotify = RegisterDeviceNotification(this->GetSafeHwnd(), amp;NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);if( !hDevNotify ) { // error handling... return FALSE;}
在看一下第8行NotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_USBSTOR;
即插即用设备由两部分组成:设备接口GUID和设备类GUID。
设备类GUID是一个广泛的设备类概念,打开“设备管理器”,在“查看”菜单中选择“依类型排序设备”,你所看到的每一个子树节点,都是一个设备类。
一个设备类GUID定义了设备类的图标,默认安全设置,安装属性(如用户不能手动安装这个类的实例,它必须列举PNP型),和其他设置。
设备类GUID并没有定义一个I/O接口,来把它作为一个分组的依据。我认为一个好的例子就是端口类。
COM设备和并口设备都是端口类的一部分,然而它们各自都有自己的I/O接口,并且互不兼容。
一个设备只能属于一个设备类。在INF文件的顶部你看到GUID,就是设备类GUID。
一个设备接口GUID定义了一组特定的I/O接口规范。
规范要求每个设备接口GUID的实例都必须支持相同的I/O接口。
每个驱动程序都要注册和启用/禁用设备接口GUID基于PnP状态。
一个设备可以注册多个设备接口GUID,它不局限于一个设备接口GUID。
如果需要,一个设备可以注册多个拥有相同设备接口GUID的事例。一个简单的I/O接口规范是键盘设备接口,键盘接口GUID的每个实例都必须支持原子输入线程。
You can see the current list of device classes and device interface classes at the following registries:
\\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class \\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\DeviceClassesA list of common device interface class GUIDs is given below:
Collapse |Copy Code
LRESULT CHWDetectDlg::OnMyDeviceChange(WPARAM wParam, LPARAM lParam){ .... .... Collapse |Copy Code Collapse |Copy Code Collapse |Copy CodeSP_PROPCHANGE_PARAMS spPropChangeParams ;spPropChangeParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);spPropChangeParams.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE ;spPropChangeParams.Scope = DICS_FLAG_GLOBAL ;spPropChangeParams.HwProfile = 0; // current hardware profilespPropChangeParams.StateChange = DICS_DISABLEif( !SetupDiSetClassInstallParams(hDevInfo, &spDevInfoData, // note we pass spPropChangeParams as SP_CLASSINSTALL_HEADER // but set the size as sizeof(SP_PROPCHANGE_PARAMS) (SP_CLASSINSTALL_HEADER*)&spPropChangeParams, sizeof(SP_PROPCHANGE_PARAMS)) ){ // handle error}else if(!SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, hDevInfo, &spDevInfoData)){ // handle error}else{ // ok, show disable success dialog // note, after that, the OS will post DBT_DEVICEREMOVECOMPLETE for the disabled device}
原文:http://www.codeproject.com/Articles/14500/Detecting-Hardware-Insertion-and-or-Removal