两种检测USB设备插入和移除的方法(Ver 2)

原创 2007年09月29日 00:18:00
说明:
这篇文章是第二个版本。最初版本发布于
http://blog.csdn.net/jingzhongrong/archive/2007/01/02/1472440.aspx两种自动检测USB设备的添加和移除的方法
最初写这篇文章是因为当时看见一篇文章提出的判断方法是不断检测盘符变化,由于有更好的实现方法,因此写下那篇文章。在CSDN论坛上发现有很多提问是关于怎样判断USB插入和移除的,因此,修改一下最初版本的文章并增加一些更详细的描述,增加一些最初版本文章中没有涉及到的内容。
 
    关于下文出现的API函数的具体使用方法请自行查阅MSDN或者上网搜索。
    对于不负责任的转载或者直接复制代码的人,我将在文章中合适的位置写入相应的版权信息(在代码中出现时我会考虑到可读性,或者前面已经有该段代码的不完整版本,或者不影响阅读的变量名等等)

jingzhongrong

本文将提供如何判断、检测USB设备的插入和移除的操作。并不提供如何卸载USB设备的描述。
    下面详细描述两种检测判断方法。
    1、这种方法大概的思路就是在一个线程中循环获得当前系统的盘符,然后获取该盘符对应的设备类型,如果是USB设备会返回DRIVE_REMOVABLE(不一定都是USB设备)。
首先我们需要几个API函数来实现:
 

API函数声明(详细用法请参见MSDN
//此函数用于获得当前磁盘驱动器盘符的位掩码
DWORD GetLogicalDrives(VOID)       
 
//在此用于检测驱动器是否已经准备完毕
BOOL GetVolumeInformation(
LPCTSTR lpRootPathName,
LPTSTR lpVolumeNameBuffer,
DWORD nVolumeNameSize,
LPDWORD lpVolumeSerialNumber,
LPDWORD lpMaximumComponentLength,
LPDWORD lpFileSystemFlags,
LPTSTR lpFileSystemNameBuffer,
DWORD nFileSystemNameSize)
  
//此函数用于获得分区信息
UINT GetDriveType(LPCTSTR lpRootPathName)

 
 
接下来,我们实现一个用于判断驱动器分区信息的函数,用来检测是否为可移动磁盘。简单的,我们直接调用GetDriveType函数:
 

代码:检测是否为可移动磁盘
int CheckDisk(char *jzrdisk)
{
 if(GetDriveType(jzrdisk)==DRIVE_REMOVABLE) return 0;
   return -1;
}

 
 
下面是检测线程的执行函数,考虑到函数GetLogicalDrives返回的是一个位码,我们需要对该返回值进行一些处理才能调用GetVolumeInformation函数检查驱动器是否已经准备完毕。由于该位掩码的最低位代表的是A盘付,我们只需在循环中简单的使用移位便可:
 

代码:操作GetLogicalDrives函数返回的位掩码
DWORD jzr = GetLogicalDrives(); 
if (jzr!=0)
{
   for (int i=0;i<26;i++)    //只判断26个盘符
    {
       if ((jzr & 1)==1)
        {
           cout<<(char)(‘A’+i)<<”盘符可用”<<endl;
        }
        jzr = jzr >>1; //判断下一个盘符
    }
}

 
 
我们在获得可用的盘符之后,便可以使用GetVolumeInformation来检测驱动器是否已经准备完毕可供我们的使用。下面是一个完整的检测函数detect的实现。用来判断是否有USB设备:
 

代码:detect函数
int detect()
{    
   char buf[10];
   DWORD jzr =GetLogicalDrives(); 
    if (jzr!=0)
    {
       for (int i=0;i<26;i++)
        {
           if ((jzr & 1)==1)
            {
               sprintf(buf,"%c",'A'+i); 
                strcat(buf,":/");
                if(!CheckDisk(buf)) 
            {
               if(GetVolumeInformation(
                     buf,0,0,0,0,0,0,0))
                    {
                       //检测到U盘并且驱动器已准备就绪
                  return 1;
                    }   
                }
             } 
             jzr = jzr >>1; 
        }
    }
    return 0;
 }

 
 
我们在线程的执行函数中:
 

代码:threadProc函数
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
   const int jingzhongrongSleeptime = 100;
   while(1)
   {
      if(detect())
      {
         //检测到USB设备
      }
      else
      {
         //没有检测到
      }
      Sleep(jingzhongrongSleeptime);
   }
}

 
 
这样便可以调用CreateThread函数创建一个检测线程了。
 
    2、对于上面的方法,存在很多很多问题,比如性能上的问题。当我们的USB设备插入或移除的时候,不能在第一时间进行判断。因此我推荐使用下面的方法。对比上面第一种实现方法,这个方法不仅实现起来简单,而且由于它是消息驱动的,性能上也比使用while循环的线程好。
    要实现这个判断,我们首先应该了解一下这个信息: WM_DEVICECHANGE。在USB设备插入或者移除等操作发生的时候,系统会将此消息分发到系统中的所有顶层窗口,我们只需要设置我们程序的消息处理函数处理该消息便可实现判断。
 
WM_DEVICECHANGE消息的wParam中有几个我们需要特别注意的值:
 

Message.wParam
DBT_DEVICEARRIVAL      0x8000 A device or piece of media has been inserted and is now available.   //插入事件
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_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.

 
 
有了上述的值,我们便可以对消息进行处理(下面的代码在BCB下编译通过,VC平台可能需要进行修改)
 

代码:消息处理
if(Message.Msg == WM_DEVICECHANGE)
{
   switch(Message.WParam)
    {
       case DBT_DEVICEARRIVAL:          //插入
       {
         ShowMessage("USB设备插入");
            ...
          break;
       }
       case DBT_DEVICEREMOVECOMPLETE:         //移除
         ShowMessage("USB设备移除");
         break;
   }
}

 
 
值得注意的是,当网络驱动器设备连接和移除的时候也会触发这个消息。这样对于我们判断是否为USB设备便产生了影响,不过,我们有办法对其进行判断:
 

代码:判断网络设备
PDEV_BROADCAST_VOLUME dbvDev = (PDEV_BROADCAST_VOLUME)
Message.LParam;
if (dbvDev->dbcv_flags & DBTF_MEDIA)
{
   ...
}

 
 
在WM_DEVICECHANGE消息的lParam参数中保存了设备的相关信息,我们要对设备的类型进行判断,只需要获得DEV_BROADCAST_VOLUME结构中的dbcv_flags的值。
当它的值为DBTF_NET时,那么当前的这个逻辑卷便是网络卷。所以我们在上面代码中判断dbcv_flags的值是否为DBTF_MEDIA,以此判断是否为网络驱动器。
 
在lParam参数中,我们还应该对这样一个信息感兴趣,这个设备是什么设备,如果我想针对某一个设备进行操作,那么,我们只需要对设备的GUID进行比较,而存放GUID信息的项出现在DEV_BROADCAST_DEVICEINTERFACE结构中的dbcc_classguid。
 

代码:获取设备GUID
DEV_BROADCAST_HDR *lpDevHdr = (DEV_BROADCAST_HDR*)
Message.LParam;  
if(lpDevHdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
{  
   DEV_BROADCAST_DEVICEINTERFACE * jzr =
        (DEV_BROADCAST_DEVICEINTERFACE*)Message.LParam;
   GUID guid = jzr ->dbcc_classguid;
}

 
如果只想对指定设备进行操作可以使用RegisterDeviceNotification函数注册,那么你只会受到你指定的设备的WM_DEVICECHANGE消息。
 
好了,我们已经把如何判断的问题解决了,下面我们只需要设置窗口的消息处理函数便可以对WM_DEVICECHANGE消息进行处理了,在BCB中,我们只要重写窗体类的WndProc虚函数:
 

代码:WndProc函数
//在头文件窗体类中添加声明
void __fastcall WndProc(TMessage &Message);
 
//cpp实现
void __fastcall TForm1::WndProc(TMessage &Message)
{
   if(Message.Msg == WM_DEVICECHANGE)
  {
      switch(Message.WParam)
      {
          case DBT_DEVICEARRIVAL:           
         {
            ShowMessage("USB设备插入");
            PDEV_BROADCAST_VOLUME jingzhongrong =
(PDEV_BROADCAST_VOLUME)Message.LParam;
             DWORD vn = jingzhongrong ->dbcv_unitmask;
               break;
          }
          case DBT_DEVICEREMOVECOMPLETE:  
            ShowMessage("USB设备移除");
            break;
      }
  }
   TForm::WndProc(Message);    //处理其他消息
}

 
 
我们已经将两种方法说明完毕,下面我们使用第二种方法实现一个简单的程序,用来判断U盘中是否含有疑似病毒的文件(这将是基于大部分U盘病毒都是有autorun.in*文件)我们获得U盘插入消息后,取得盘符并对U盘盘符根目录进行查找是否有文件包含“autorun”字符串,并列出包含有该字符串的文件名,提示用户注意。
程序在Windows 2003 Server + Borland Developer C++Builder 2006中调试通过。
下面是程序清单:
 

头文件Unit1.h
//$$---- Form HDR ----
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <windows.h>
#include <ExtCtrls.hpp>
#include <XPMan.hpp>
//---------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
  TMemo *Memo1;
  TButton *Button1;
  TButton *Button2;
  TXPManifest *XPManifest1;
  TTrayIcon *TrayIcon1;
  void __fastcall Button1Click(TObject *Sender);
  void __fastcall Button2Click(TObject *Sender);
private: // User declarations
  bool __fastcall HasAutoRunInFileName(unsigned long area);
  char __fastcall FirstDriveFromMask(ULONG unitmask);
  bool started;
public: // User declarations
  __fastcall TForm1(TComponent* Owner);
  void __fastcall WndProc(TMessage &Message);
};
//---------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------
#endif

 

CPP文件
//$$---- Form CPP ----
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include <windows.h>
#include <dir.h>
#include <io.h>
#include <fcntl.h>
#include <Dbt.h>
//---------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
 : TForm(Owner),started(false)
{
}
//---------------------------------------------------------------
void __fastcall TForm1::WndProc(TMessage &Message)
{
  if(started)
  {
     if(Message.Msg == WM_DEVICECHANGE)
     {
         switch(Message.WParam)
         {
             case DBT_DEVICEARRIVAL:
             {
               PDEV_BROADCAST_VOLUME dbvDev =
 (PDEV_BROADCAST_VOLUME)Message.LParam;
                DWORD vn = dbvDev->dbcv_unitmask;
                this->HasAutoRunInFileName(vn);
                 break;
              }
             case DBT_DEVICEREMOVECOMPLETE: 
               ShowMessage(“by jingzhongrong”);
               break;
         }
     }
  }
  TForm::WndProc(Message);
}
//---------------------------------------------------------------
bool __fastcall TForm1::HasAutoRunInFileName(unsigned long area)
{
  char s = FirstDriveFromMask(area);
  struct ffblk ffblk;
  int done;
  AnsiString path = AnsiString(s);
  path += ":/*autorun*";
  done = findfirst(path.c_str(),&ffblk,0);
  Memo1->Clear();
  while (!done)
  {
     Memo1->Lines->Add(AnsiString(ffblk.ff_name));
     done = findnext(&ffblk);
  }
  if(Memo1->Lines->Count != 0)
  {
     MessageBox(Handle,"注意,U盘中含有包含"autorun"字样的文件,可能是病毒请不要直接双击打开该U","jzrUSBAutoRunScan",MB_OK | MB_ICONHAND);
     return true;
  }
  return false;
}
//---------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  started = true;  //开始监视
   Button2->Enabled = true;
  Button1->Enabled = false;
}
//---------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
  started = false;  //停止监视
  Button2->Enabled = false;
  Button1->Enabled = true;
}
//---------------------------------------------------------------
char __fastcall TForm1::FirstDriveFromMask(ULONG unitmask)
{
   char i;
   for (i = 0; i < 26; ++i)
   {
       if (unitmask & 0x1)
          break;
       unitmask = unitmask >> 1;
   }
  return (i + 'A');
}
//---------------------------------------------------------------
 

 
本文到此结束,写于2007年9月29日星期六,jingzhongrong ,广州

 

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

检测USB设备的插入和拔出

1.需求 需要一个DLL,它可以自动检测USB设备插入和拔出,并以回调函数的方法告诉调用DLL的应用程序。   2.预研 2.1参考文档 (1)关于“Registering for Devi...
  • buyicn
  • buyicn
  • 2015-06-21 23:48
  • 4035

C#:基于WMI监视USB插拔

作者:Splash 转自:http://blog.csdn.net/jhqin/article/details/6698411 参考资料: USB Port Insert / Remove ...

C++监控USB设备

这个程序前阵子帮一个朋友实现的,之前从未用消息队列做过类似的事情,做完后感觉其在线程同步,通信发面很好用,难怪COM也用这套机制。 程序稍微修改便能用作一般性的处理,目前实现的功能类似于监控Wind...

mfc检测usb插拔事件

1.在OnInitDialog()函数添加注册代码,头文件#include (OnInitDialog()可以通过ctrl+shift+x添加) static const GUID GUID_DEVI...

USB 设备热插拔的检测

系统检测USB 设备往往分为两个过程: 1. USB 设备已经插入, 打开软件。 此时需要枚举当前设备列表中所有的设备,并过滤指定的USB 设备。 2. 打开软件后, USB 热插拔 。 ...

WM_DEVICECHANGE 检测USB的热拔插事件

WM_DEVICECHANGE的使用方式有二種,一種是透過WindowProc()的方式,一種是直接在程式裡加上OnDeviceChange()的函數,底下就先介紹第二種方式。 1. 新增消息 --...

一个可靠的USB设备插拔检测

做USB设备插拔检测时, DBT_DEVICEREMOVECOMPLETE和DBT_DEVICEARRIVAL来的次数总是多几次, 这个代码不错:  http://www.codeproject.co...

USB学习笔记二《插拔检测+示例代码》

USB热插拔检测: 方法一: 启动定时器,定时轮询注册表,这种方法容易死机。 方法二: 添加Windows消息 ON_WM_DEVICECHANGE()映射。 步骤: 一: 要想添加消息映...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)