关于u盘识别(OnDeviceChange方式)的几个问题,请教!

时间:2021-07-18 07:03:48
烦请高手帮忙,这个问题把我弄得好憔悴,我的代码骨架如下:

 


LRESULT CHWDetectDlg::OnMyDeviceChange(UINT nEventType,DWORD dwData)
{
    PDEV_BROADCAST_HDR dbd = (PDEV_BROADCAST_HDR) dwData;   
    switch (nEventType)
    {
       case DBT_DEVICEREMOVECOMPLETE:
            ...
            break;
       case DBT_DEVICEARRIVAL:  // 问题点在这个分支下
            switch( dbd ->dbch_devicetype )
            {
               case DBT_DEVTYP_DEVICEINTERFACE:
                    PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)dbd ;
                    // 这种情况下如何读出盘符???
                    break;
               case DBT_DEVTYP_VOLUME:
                    PDEV_BROADCAST_VOLUME pDevInf = (PDEV_BROADCAST_VOLUME)dbd ;
                    
                    break;

              default:
                    break;

            }

             ...
            break;
       default:
            break;
    }

return TRUE;

}





问题一、在响应u盘的插入消息后(DBT_DEVICEARRIVAL),在判断dbd ->dbch_devicetype 时,网上找的代码都直接走到“case DBT_DEVTYP_VOLUME:“这个分支,而我自己的代码却走向却是”case DBT_DEVTYP_DEVICEINTERFACE:”这种情况。为什么同样的事件,触发的逻辑走向不一样呢?

问题二、在”case DBT_DEVTYP_DEVICEINTERFACE:”这种情况下,我想得到插入u盘的盘符,我该怎样实现呢??

问题三、在设备注册过程中,代码如下:
DEV_BROADCAST_DEVICEINTERFACE DevInt;
memset(&DevInt,0,sizeof(DEV_BROADCAST_DEVICEINTERFACE));
DevInt.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
DevInt.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
DevInt.dbcc_classguid = GetCurrentUSBGUID();//m_usb->GetDriverGUID();
if (!RegisterDeviceNotification(m_hWnd, &DevInt,DEVICE_NOTIFY_WINDOW_HANDLE) )
   return FALSE;

红色字体行,“dbcc_devicetype ”可改成其他值吗,比如PDEV_BROADCAST_VOLUME,还是固定值???

感谢!

14 个解决方案

#1


who can help me ?

#2


该回复于2012-08-02 09:23:29被版主删除

#3


help

#4


怎么不重载WindowProc?

LRESULT CXXXDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
{
// TODO: Add your specialized code here and/or call the base class
DWORD ThreadId;
bool allzero=true;
size_t i;

if(message!=WM_DEVICECHANGE)
{
return CDialog::WindowProc(message, wParam, lParam);
}

if(wParam==DBT_DEVICEARRIVAL)//有新设备插入系统
{
DEV_BROADCAST_HDR* pDev=(DEV_BROADCAST_HDR*)lParam;
if(pDev->dbch_devicetype!=DBT_DEVTYP_VOLUME )//移动存储设备
{
return CDialog::WindowProc(message, wParam, Param);
}

DEV_BROADCAST_VOLUME* pDisk=(DEV_BROADCAST_VOLUME*)lParam;
DWORD mask=pDisk->dbcv_unitmask;

TCHAR diskname[MAX_PATH];
for(i=0;i<32;i++)
{
if((mask>>i)==1)
{//获取盘符
diskname[0]='A'+i;
diskname[1]='\0';
_tcscat_s(diskname,TEXT(":\\"));
break;
}
}
}
}

#5


1. 如果注册时传递 服务状态句柄而不是 窗口句柄,那么可以在 case DBT_DEVTYP_DEVICEINTERFACE中获得
   需要多注册一个GUID (GUID_DEVINTERFACE_VOLUME - { 0x53f5630d, 0xb6bf, 0x11d0, { 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b })

2. 如果是窗口句柄,那么在 case DBT_DEVTYP_VOLUME 中获得。如四楼所示

#6


我先自己回答我自己的第三个问题,不过不是很确定,一来算帮帮其他人,二来如果后面有人有一个更权威的答案欢迎指出。

下面的来自msdn:

The DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE events are automatically broadcast to all top-level windows for port devices. Therefore, it is not necessary to call RegisterDeviceNotification for ports, and the function fails if the dbch_devicetype member is DBT_DEVTYP_PORT.  Volume notifications are also broadcast to top-level windows, so the function fails if dbch_devicetype is DBT_DEVTYP_VOLUME. OEM-defined devices are not used directly by the system, so the function fails if dbch_devicetype is DBT_DEVTYP_OEM.

#7


读出盘符 我记得好像有API的,记得不太清楚。

#8


引用 4 楼  的回复:
怎么不重载WindowProc?

C/C++ code


LRESULT CXXXDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
{
    // TODO: Add your specialized code here and/or call the base class
    DWORD Threa……



谢谢回复,你的代码实现我要的“识别u盘盘符”功能一点问题都没有()。

但是我花了这么多时间,我现在确实想知道为什么我的方法如何进行下去。

我再描述一下我的情况

在OnInitDialog()中我用的是如下代码注册:



HDEVNOTIFY hDevNotify;
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
ZeroMemory( &NotificationFilter, sizeof(NotificationFilter));
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
NotificationFilter.dbcc_devicetype DBT_DEVTYP_DEVICEINTERFACE;
for(int i=0; i<sizeof(GUID_DEVINTERFACE_LIST)/sizeof(GUID); i++) 
{
NotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_LIST[i]; hDevNotify = RegisterDeviceNotification(this->GetSafeHwnd(), 
                                &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
if( !hDevNotify ) 
{
return FALSE;
}
}


其中GUID_DEVINTERFACE_LIST[]是:

static const GUID GUID_DEVINTERFACE_LIST[] = 
{
// GUID_DEVINTERFACE_USB_DEVICE
{ 0xA5DCBF10, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } },
   
//GUID_DEVINTERFACE_VOLUME
{0x53f5630d, 0xb6bf, 0x11d0, { 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b }},

// GUID_DEVINTERFACE_DISK
{ 0x53f56307, 0xb6bf, 0x11d0, { 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b } },

// GUID_DEVINTERFACE_HID, 
{ 0x4D1E55B2, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } },

// GUID_NDIS_LAN_CLASS
{ 0xad498944, 0x762f, 0x11d0, { 0x8d, 0xcb, 0x00, 0xc0, 0x4f, 0xc3, 0x35, 0x8c } }
};


手动添加的ondevicechange事件实现代码如我帖子开篇所示:


PDEV_BROADCAST_HDR pDevBroadcastHdr=(PDEV_BROADCAST_HDR)dwData;   
 
switch(nEventType)
{
case DBT_DEVICEARRIVAL:
if (pDevBroadcastHdr->dbch_devicetype==DBT_DEVTYP_VOLUME) 

  PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)            pDevBroadcastHdr;
 这种情况下我知道通过lpdbv ->dbcv_unitmask实现盘符的获知;
}
else if(pDevBroadcastHdr->dbch_devicetype==DBT_DEVTYP_DEVICEINTERFACE)
{
  谁能回答我在这种情况下,我通过什么方式得知u盘盘符呢???
}
break;
case DBT_DEVICEREMOVECOMPLETE:
                        。。
default:
break;
}



#9


引用 5 楼  的回复:
1. 如果注册时传递 服务状态句柄而不是 窗口句柄,那么可以在 case DBT_DEVTYP_DEVICEINTERFACE中获得
  需要多注册一个GUID (GUID_DEVINTERFACE_VOLUME - { 0x53f5630d, 0xb6bf, 0x11d0, { 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b })

2. 如果……


谢谢回复哦

一、“1. 如果注册时传递 服务状态句柄而不是 窗口句柄,那么可以在 case DBT_DEVTYP_DEVICEINTERFACE中获得”,请问可以通过什么方式获得盘符呢?
二、我通过以下方式注册时,

HDEVNOTIFY hDevNotify;
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
ZeroMemory( &NotificationFilter, sizeof(NotificationFilter));
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
NotificationFilter.dbcc_devicetype =/*DBT_DEVTYP_VOLUME*/ DBT_DEVTYP_DEVICEINTERFACE;
for(int i=0; i<sizeof(GUID_DEVINTERFACE_LIST)/sizeof(GUID); i++) 
{
NotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_LIST[i];
hDevNotify = RegisterDeviceNotification(this->GetSafeHwnd(), &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
if( !hDevNotify ) 
{
return FALSE;
}
}


我的代码可以流向楼上代码的else if(pDevBroadcastHdr->dbch_devicetype==DBT_DEVTYP_DEVICEINTERFACE)段,

但我把注册代码注释掉时,调试过程中却进了if (pDevBroadcastHdr->dbch_devicetype==DBT_DEVTYP_VOLUME)段

不知是什么原因??

#10


9楼,你在6楼都引用了
Volume notifications are also broadcast to top-level windows
应该是,即使不注册,也会发送该消息。

而且注册 DBT_DEVTYP_DEVICEINTERFACE 后,首先进入的是  DBT_DEVTYP_DEVICEINTERFACE 分支,因为是U盘,所以有“USB设备”“磁盘类”的子消息进来,根据 8楼的GUID数组,跟着还是会进入 DBT_DEVTYP_VOLUME 的

至于9楼第一个问题,这个说起来就麻烦了,你用窗口消息的话,就不用考虑这个途径了。

#11


马上结贴

我的第一个帖子,谢谢你们

#12


遇到跟你一样的问题,年前的代码还能够获得 DBT_DEVTYP_VOLUME
年后回来编译,发现获得的是DBT_DEVTYP_DEVICEINTERFACE了,代码没有改动过!!!

#13


我也遇到跟楼主一模一样的问题。当收到这两种消息时,进行重载。但是用四楼的方法行不通哎,再者,我的U盘插入时只能发出DBT_DEVTYP_DEVICEINTERFACE消息,程序收不到DBT_DEVTYP_VOLUME消息,这样就进入死循环了啊。怎么解决呢?
当只收到DBT_DEVTYP_DEVICEINTERFACE消息的时候怎么得到U盘盘符呢?楼主最后怎么解决的呢??

#14


这个问题还没解决?我也碰到这个问题了

#1


who can help me ?

#2


该回复于2012-08-02 09:23:29被版主删除

#3


help

#4


怎么不重载WindowProc?

LRESULT CXXXDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
{
// TODO: Add your specialized code here and/or call the base class
DWORD ThreadId;
bool allzero=true;
size_t i;

if(message!=WM_DEVICECHANGE)
{
return CDialog::WindowProc(message, wParam, lParam);
}

if(wParam==DBT_DEVICEARRIVAL)//有新设备插入系统
{
DEV_BROADCAST_HDR* pDev=(DEV_BROADCAST_HDR*)lParam;
if(pDev->dbch_devicetype!=DBT_DEVTYP_VOLUME )//移动存储设备
{
return CDialog::WindowProc(message, wParam, Param);
}

DEV_BROADCAST_VOLUME* pDisk=(DEV_BROADCAST_VOLUME*)lParam;
DWORD mask=pDisk->dbcv_unitmask;

TCHAR diskname[MAX_PATH];
for(i=0;i<32;i++)
{
if((mask>>i)==1)
{//获取盘符
diskname[0]='A'+i;
diskname[1]='\0';
_tcscat_s(diskname,TEXT(":\\"));
break;
}
}
}
}

#5


1. 如果注册时传递 服务状态句柄而不是 窗口句柄,那么可以在 case DBT_DEVTYP_DEVICEINTERFACE中获得
   需要多注册一个GUID (GUID_DEVINTERFACE_VOLUME - { 0x53f5630d, 0xb6bf, 0x11d0, { 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b })

2. 如果是窗口句柄,那么在 case DBT_DEVTYP_VOLUME 中获得。如四楼所示

#6


我先自己回答我自己的第三个问题,不过不是很确定,一来算帮帮其他人,二来如果后面有人有一个更权威的答案欢迎指出。

下面的来自msdn:

The DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE events are automatically broadcast to all top-level windows for port devices. Therefore, it is not necessary to call RegisterDeviceNotification for ports, and the function fails if the dbch_devicetype member is DBT_DEVTYP_PORT.  Volume notifications are also broadcast to top-level windows, so the function fails if dbch_devicetype is DBT_DEVTYP_VOLUME. OEM-defined devices are not used directly by the system, so the function fails if dbch_devicetype is DBT_DEVTYP_OEM.

#7


读出盘符 我记得好像有API的,记得不太清楚。

#8


引用 4 楼  的回复:
怎么不重载WindowProc?

C/C++ code


LRESULT CXXXDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
{
    // TODO: Add your specialized code here and/or call the base class
    DWORD Threa……



谢谢回复,你的代码实现我要的“识别u盘盘符”功能一点问题都没有()。

但是我花了这么多时间,我现在确实想知道为什么我的方法如何进行下去。

我再描述一下我的情况

在OnInitDialog()中我用的是如下代码注册:



HDEVNOTIFY hDevNotify;
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
ZeroMemory( &NotificationFilter, sizeof(NotificationFilter));
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
NotificationFilter.dbcc_devicetype DBT_DEVTYP_DEVICEINTERFACE;
for(int i=0; i<sizeof(GUID_DEVINTERFACE_LIST)/sizeof(GUID); i++) 
{
NotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_LIST[i]; hDevNotify = RegisterDeviceNotification(this->GetSafeHwnd(), 
                                &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
if( !hDevNotify ) 
{
return FALSE;
}
}


其中GUID_DEVINTERFACE_LIST[]是:

static const GUID GUID_DEVINTERFACE_LIST[] = 
{
// GUID_DEVINTERFACE_USB_DEVICE
{ 0xA5DCBF10, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } },
   
//GUID_DEVINTERFACE_VOLUME
{0x53f5630d, 0xb6bf, 0x11d0, { 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b }},

// GUID_DEVINTERFACE_DISK
{ 0x53f56307, 0xb6bf, 0x11d0, { 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b } },

// GUID_DEVINTERFACE_HID, 
{ 0x4D1E55B2, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } },

// GUID_NDIS_LAN_CLASS
{ 0xad498944, 0x762f, 0x11d0, { 0x8d, 0xcb, 0x00, 0xc0, 0x4f, 0xc3, 0x35, 0x8c } }
};


手动添加的ondevicechange事件实现代码如我帖子开篇所示:


PDEV_BROADCAST_HDR pDevBroadcastHdr=(PDEV_BROADCAST_HDR)dwData;   
 
switch(nEventType)
{
case DBT_DEVICEARRIVAL:
if (pDevBroadcastHdr->dbch_devicetype==DBT_DEVTYP_VOLUME) 

  PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)            pDevBroadcastHdr;
 这种情况下我知道通过lpdbv ->dbcv_unitmask实现盘符的获知;
}
else if(pDevBroadcastHdr->dbch_devicetype==DBT_DEVTYP_DEVICEINTERFACE)
{
  谁能回答我在这种情况下,我通过什么方式得知u盘盘符呢???
}
break;
case DBT_DEVICEREMOVECOMPLETE:
                        。。
default:
break;
}



#9


引用 5 楼  的回复:
1. 如果注册时传递 服务状态句柄而不是 窗口句柄,那么可以在 case DBT_DEVTYP_DEVICEINTERFACE中获得
  需要多注册一个GUID (GUID_DEVINTERFACE_VOLUME - { 0x53f5630d, 0xb6bf, 0x11d0, { 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b })

2. 如果……


谢谢回复哦

一、“1. 如果注册时传递 服务状态句柄而不是 窗口句柄,那么可以在 case DBT_DEVTYP_DEVICEINTERFACE中获得”,请问可以通过什么方式获得盘符呢?
二、我通过以下方式注册时,

HDEVNOTIFY hDevNotify;
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
ZeroMemory( &NotificationFilter, sizeof(NotificationFilter));
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
NotificationFilter.dbcc_devicetype =/*DBT_DEVTYP_VOLUME*/ DBT_DEVTYP_DEVICEINTERFACE;
for(int i=0; i<sizeof(GUID_DEVINTERFACE_LIST)/sizeof(GUID); i++) 
{
NotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_LIST[i];
hDevNotify = RegisterDeviceNotification(this->GetSafeHwnd(), &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
if( !hDevNotify ) 
{
return FALSE;
}
}


我的代码可以流向楼上代码的else if(pDevBroadcastHdr->dbch_devicetype==DBT_DEVTYP_DEVICEINTERFACE)段,

但我把注册代码注释掉时,调试过程中却进了if (pDevBroadcastHdr->dbch_devicetype==DBT_DEVTYP_VOLUME)段

不知是什么原因??

#10


9楼,你在6楼都引用了
Volume notifications are also broadcast to top-level windows
应该是,即使不注册,也会发送该消息。

而且注册 DBT_DEVTYP_DEVICEINTERFACE 后,首先进入的是  DBT_DEVTYP_DEVICEINTERFACE 分支,因为是U盘,所以有“USB设备”“磁盘类”的子消息进来,根据 8楼的GUID数组,跟着还是会进入 DBT_DEVTYP_VOLUME 的

至于9楼第一个问题,这个说起来就麻烦了,你用窗口消息的话,就不用考虑这个途径了。

#11


马上结贴

我的第一个帖子,谢谢你们

#12


遇到跟你一样的问题,年前的代码还能够获得 DBT_DEVTYP_VOLUME
年后回来编译,发现获得的是DBT_DEVTYP_DEVICEINTERFACE了,代码没有改动过!!!

#13


我也遇到跟楼主一模一样的问题。当收到这两种消息时,进行重载。但是用四楼的方法行不通哎,再者,我的U盘插入时只能发出DBT_DEVTYP_DEVICEINTERFACE消息,程序收不到DBT_DEVTYP_VOLUME消息,这样就进入死循环了啊。怎么解决呢?
当只收到DBT_DEVTYP_DEVICEINTERFACE消息的时候怎么得到U盘盘符呢?楼主最后怎么解决的呢??

#14


这个问题还没解决?我也碰到这个问题了