在.NET中探测U盘的插入/拔出

本文介绍如何使用C#监听USB设备的插入和拔出操作。通过捕获WM_DEVICECHANGE消息并解析DEV_BROADCAST_HDR和DEV_BROADCAST_VOLUME结构体,可以实现对设备变化的响应。文中提供了一个具体的示例代码。
有同学向我问这个问题,于是就Google了一下找到答案,不过是C下的,我将其改编成了C#的。

  当设备被插入/拔出的时候,WINDOWS会向每个窗体发送WM_DEVICECHANGE 消息,当消息的wParam 值等于 DBT_DEVICEARRIVAL 时,表示Media设备被插入并且已经可用;如果wParam值等于DBT_DEVICEREMOVECOMPLETE,表示Media设备已经被移出。

它们的lParam都指向一个 DEV_BROADCAST_HDR结构体,其原形如下:

1 typedef struct _DEV_BROADCAST_HDR
2 {
3     DWORD dbch_size;
4     DWORD dbch_devicetype;
5     DWORD dbch_reserved;
6 } DEV_BROADCAST_HDR, *PDEV_BROADCAST_HDR;

这个结构体仅仅是一个“头”(HDR),其后还有附加数据,dbch_size表示结构体实例的字节数,当其中的dbch_devicetype字段值等于DBT_DEVTYP_VOLUME时,表示当前设备是逻辑驱动器,且lParam实际上指向的应该是DEV_BROADCAST_VOLUME 结构体实例(真佩服这种逻辑),DEV_BROADCAST_VOLUME 结构体原形如下:

1 typedef struct _DEV_BROADCAST_VOLUME {
2     DWORD dbcv_size;
3     DWORD dbcv_devicetype;
4     DWORD dbcv_reserved;
5     DWORD dbcv_unitmask;
6     WORD dbcv_flags;
7 } DEV_BROADCAST_VOLUME, *PDEV_BROADCAST_VOLUME;
其中dbcv_unitmask 字段表示当前改变的驱动器掩码,第一位表示驱动器号A,第二位表示驱动器号B,第三位表示驱动器号C,以此类推…… dbcv_flags 表示驱动器的类别,如果等于1,则是光盘驱动器;如果是2,则是网络驱动器;如果是硬盘、U盘则都等于0

  所以,我只需要在程序中捕捉WM_DEVICECHANGE 消息,然后根据具体情况去处理即可,下面是我的测试代码:

using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace UDiskDetect
{
    
public partial class Form1 : Form
    
{
        
public Form1()
        
{
            InitializeComponent();
        }


        
private void Form1_Load(object sender, EventArgs e)
        
{

        }


        [StructLayout(LayoutKind.Sequential)]
        
struct DEV_BROADCAST_HDR
        
{
            
public UInt32 dbch_size;
            
public UInt32 dbch_devicetype;
            
public UInt32 dbch_reserved;
        }


        [StructLayout(LayoutKind.Sequential)]
        
struct DEV_BROADCAST_VOLUME
        
{
            
public UInt32 dbcv_size;
            
public UInt32 dbcv_devicetype;
            
public UInt32 dbcv_reserved;
            
public UInt32 dbcv_unitmask;
            
public UInt16 dbcv_flags;
        }


        
protected override void DefWndProc(ref Message m)
        
{
            
if (m.Msg == 0x0219)//WM_DEVICECHANGE
            {
                
switch (m.WParam.ToInt32())
                
{
                    
case 0x8000://DBT_DEVICEARRIVAL
                        {
                            DEV_BROADCAST_HDR dbhdr 
= (DEV_BROADCAST_HDR)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_HDR));

                            
if (dbhdr.dbch_devicetype == 0x00000002)//DBT_DEVTYP_VOLUME
                            {
                                DEV_BROADCAST_VOLUME dbv 
= (DEV_BROADCAST_VOLUME)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_VOLUME));
                                
if (dbv.dbcv_flags == 0)
                                    AddVolumes(GetVolumes(dbv.dbcv_unitmask));
                            }

                            
break;
                        }

                    
case 0x8004://DBT_DEVICEREMOVECOMPLETE
                        {
                            DEV_BROADCAST_HDR dbhdr 
= (DEV_BROADCAST_HDR)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_HDR));

                            
if (dbhdr.dbch_devicetype == 0x00000002)//DBT_DEVTYP_VOLUME
                            {
                                DEV_BROADCAST_VOLUME dbv 
= (DEV_BROADCAST_VOLUME)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_VOLUME));
                                
if (dbv.dbcv_flags == 0)
                                    RemoveVolumes(GetVolumes(dbv.dbcv_unitmask));
                            }

                            
break;
                        }

                }

            }

            
base.DefWndProc(ref m);
        }


        
/// <summary>
        
/// 根据驱动器掩码返回驱动器号数组
        
/// </summary>
        
/// <param name="Mask">掩码</param>
        
/// <returns>返回驱动器号数组</returns>

        public static char[] GetVolumes(UInt32 Mask)
        
{
            List
<char> Volumes = new List<char>();

            
for (int i = 0; i < 32; i++)
            
{
                
uint p = (uint)Math.Pow(2, i);
                
if ((p | Mask) == p)
                
{
                    Volumes.Add((
char)('A' + i));
                }

            }


            
return Volumes.ToArray();
        }


        
public void AddVolumes(char[] Volumes)
        
{
            
foreach (char volume in Volumes)
                listBox1.Items.Add(volume);
        }


        
public void RemoveVolumes(char[] Volumes)
        
{
            
foreach (char volume in Volumes)
                listBox1.Items.Remove(volume);
        }


    }

}
 
绍了USB Type-C接口的定义,重点解析了CC逻辑芯片和E-Mark芯片的作用,包括CC引脚的功能、设备是否需要CC逻辑芯片,以及与雷电接口的关系。同时,讨论了如何准确识别Type-C接口的标识,帮助读者理解Type-C接口的特性和应用场景。摘要由优快云通过智能技术生成TYPEC浅析从USB接口出现以来,相信很多人都遇到这一个问题——不论怎么熟悉你的电脑、USB接口位置,一般都没有一次就能找对方向插上,很大概率都要换各边重复插一次!对于个人来讲,特别是有点强迫症的工程师来说,真的难以接受这个事实!所以硬件工程师的位置上宁愿有很多的线缆或者USB-HUB,不为别的,就为了电脑一开机只要连一个HUB线,其它的USB数据线、U、USB设备等等,都统一接上,不需要再逐个去连接,不然一个个连(而且又不能一下插上的话),真的很浪费时间(而且很不爽)!……额,说着说着就说多了。我们切回正题吧。因最近做了个项目,虽说都是用的USB2.0(原来用的都是Micro USB/ Mini USB……接口),为了跟上时代的步伐,都上了TYPEC物理接口。原来的USB接口都只有最多5个信号VUBS/D-/D+/ID/GND,但是TYPEC竟然有N多的pin角,带着工程师应有的好奇心,要把这个整明白。早期有一篇DIY解刨TYPEC的线的文字<<TYPEC OTG与MICRO OTG线的区别>>有做了简单的介绍,当时也说了后需要发一篇关于TYPEC的解读文章,这里也是兑现下承诺吧。整理这篇文章真的是找了不少论坛资料,也看了不少大牛的文章才理解出来的,一些很牛X的技术,我也不多说了。(太深入和牛X的我也没研究过哈,担心说着说着大家都信了……).       这里我就总结下个人的几个问题点,然后按照问题答疑的方式给讲解下(基本定义姓百的大哥上面很多,这里我也汇总下,毕竟以后自己也可以看看,我希望这篇文章可以解决大部分新手的疑问!):1.TYPEC接口定义TYPEC插头PLUG引脚定义(Front View):TYPEC母头RECEPTOR引脚定义(Front View):       前文提过二则最大区别在于PIN数是不一样的,从下表可以看出差异。因为TYPEC协议将最大支持到20W甚至更大的功率,所以其Vbus引脚和GND引脚也特别多。另外,因为TYPEC还要支持高速、DP等协议,所以也多了其它功能的引脚。特别是其中的CC引脚。USB Type-C接口包含的2个通道配置(Channel Configuration)信号引脚(CC1 & CC2),用于功能协商。上述信号确定接口插入方向,并用于协商接口上的供电功能、替代模式和外设模式。【后面详细说明下CC的疑问】****************************************插入知识点******************************************通常用到的USB2.0的TYPEC线插头和Micro USB线插头的PIN对应关系****************************************插入知识点*****************************************CC引脚在DPF和UFP内部分别需要有上拉/下拉电阻,用来识别各自的功能。根据不同的上下拉电阻检测,可以确认负载信息。在DFP内部CC通过不同Rp上拉到电源。在UFP内部CC通过5.1K电阻下拉到GND。下行DFP设备必须可以区分是下拉电阻Rd还是负载Ra接入来确认是否有上行UFP设备接入并判断是否需要给Vconn供电。DFP设备只有检测到Ra时,才给Vconn供电。全功能TYPEC线缆内部是有CC芯片的,需要通过CC2(Vconn)对其供电。设备在给Vconn供电前,电源线缆在Vconn引脚需要有个Ra电阻(Ra表示Vconn对地负载,应该是线缆内部本身接Ra,设备根据读取线缆上面的信息来判断线缆的属性)。在某些线缆上,这有可能是个纯粹的电阻,也有可能是简单的负载。****************************************插入知识点********************************************DFP直的是下行端口,可以认为是HOST设备、电源适配器这类。UFP可以认为是从设备DEVICE,U、受电设备等。*******************************************插入知识点*********************************************************************************插入知识点*****************************************所以在文章<<TYPEC OTG与MICRO OTG线的区别>>中有解释说如果是单纯的将TYPEC-OTG线用在MICRO-OTG上是不行的,因为MICRO-OTG的ID引脚定义不同。所以要强制修改的话,需要将TYPEC-OTG内部的CC的下拉5.1K电阻,改为0R短路到GND,而且把这个信号对应到原先Micro的ID引脚信号上。下面是Micro-B Receptor转USB C plug的数转接线线信号图,可以参考一二。****************************************插入知识点*****************************************2.CC解读(Current Configuration)刚刚开始接触TYPEC时,经常看到CC这个引脚功能。开始觉得CC是个单纯的信号、有时候又理解为CC应该是类似单总线功能有数据通讯、又或者有是串口功能……这些理解都有问题。经过一些了解后,初步汇总如下,希望对大家也有帮助。若有高手过目,也请指点一二。CC实际有CC1和CC2。在插座上两个信号都有接。但是在插头上面一般只有CC1或者CC1&Vconn.设备通过CC1&CC2上的Rd/Ra来判断不同的功能,是否有负载接入、需要提供多达的电流、是否需要供电等信息。在DFP的CC pin有上拉电阻Rp,在UFP有下拉电阻Rd。未连接时,DFP的VBUS是无输出的。连接后CC pin相连,DFP的CC pin会检测到UFP的下拉电阻Rd,说明连接上了,DFP就打开Vbus电源开关,输出电源给UFP。而哪个CC pin(CC1,CC2)检测到下拉电阻就确定接口插入的方向,顺便切换RX/TX。比如在全功能TYPEC接口情况下:CC1&CC2都接下拉电阻Rd时,表示Debug accessory 模式;CC1&CC2都接Ra时表示Audio Adapter Accessory Mode (虽然还不懂是啥。。。但是手册是这么解的….);CC1接Rd、CC2为NC时(或者反之),表示有负载接入;CC1接Ra,CC2为NC时(或者反之),表示供电线缆接入,且没接负载(表示给Vconn供电,无需给VBUS供电);CC1接Rd、CC2接Ra时,表示有供电线缆接入,而且接着负载(给VBUS供电,同时CC2作为Vconn给线缆内部芯片供电5V,CC1传信号给UFP);或者是UFP(上行端口,即从设备)接入(表示需要给VBUS供电)考虑另外,对于需要广播电流输出能力的DFP而言,需要通过不同值的CC上拉电阻Rp来实现;对于UFP而言,需要检测CC管脚上的电压值来获取对方DFP的电流输出能力。****************************************插入知识点********************************************在DFP的CC pin有上拉电阻Rp,在UFP有下拉电阻Rd。未连接时,DFP的VBUS是无输出的。(所以通常淘宝上面买到的TYPEC转Micro的头,其只有VCC/D+/D-/GND信号连接,CC是没连的。故将Micro转接头接在带有PD的TYPEC电源线上后,VBUS就无输出,Micro端VBUS没有供电)。****************************************插入知识点**********************************************根据标准下拉电阻为Rd=5.1k,上拉电阻Rp为不确定的值。USB Type-C靠Rp的不同,从而在 CC pin检测到的电压就不一样,来控制DFP供电模式。在TYPEC中两个CC,实际上在不含芯片的线缆里只有一根cc线。含芯片的线缆也不是两根cc线,而是一根cc,一根Vconn,用来给线缆里的芯片供电(5V),这时就cc端没有下拉电阻Rd,而是下拉电阻Ra:800-1200Ω。****************************************插入知识点*****************************************CC1和CC2是两个关键引脚,作用很多:探测连接,区分正反面,区分DFP和UFP;实际上在不含芯片的线缆里只有一根cc线。含芯片的线缆也不是两根cc线,而是一根cc,一根Vconn,用来给线缆里的芯片供电(5V)。这时就cc端没有下拉电阻Rd,而是下拉电阻Ra.(即当线缆里有芯片的时一个cc传输信号,一个cc变成Vconn供电);支持PD的设备必须采用CC Logic芯片。USB Type-C目前支持最高20V/5A,此时必须要支持USB PD,即需要额外的PD芯片,所以不要以为是USB Type-C接口就可以支持到20V/5A。****************************************插入知识点*****************************************3.设备是否需要CC逻辑芯片所有全功能的Type-C电缆都应该封装有E-Marker:封装有E-Marker芯片的USB Type-C有源电缆,DFP和UFP利用PD协议可以读取该电缆的属性:电源传输能力,数据传输能力,ID等信息;但USB2.0 Type-C电缆可以不封装E-Marker。所有的DFP设备需要CC逻辑检测与控制芯片:DFP需要检测到CC管脚上有某个电压时,判断UFP设备已插入拔出,来提供和管理VBUS。当没有UFP设备插入时,必须关闭VBUS。USB3.0/USB3.1应用中,除UFP设备以外的所有设备都需要CC逻辑检测与控制芯片:在USB2.0应用中,无需考虑方向检测问题,但USB3.0或者USB3.1应用中,必须考虑方向检测问题(有一种情况例外,比如U,移动硬等可以不考虑方向,不用CC芯片。)。持PD的设备必须采用CC Logic芯片:对于UFP而言,需要检测CC管脚上的电压值来获取对方DFP的电流输出能力。USB PD看似只是电源传输与管理的协议,实际上它可改变端口角色,可与有源电缆通讯,允许DFP成为受电设备等诸多高级功能。所有全功能的Type-C电缆都应该封装有E-Marker,但USB2.0 Type-C电缆可以不封装E-Marker综上,只有因为功耗较低而不需要检测电流能力的UFP(U,耳机,鼠标等)不需要CC逻辑检测端口控制芯片外,其余所有的DFP、DRP(如电脑,手机,平板,移动电源)、需要检测DFP电流输出能力的UFP、支持PD的设备,都需要CC逻辑检测与端口控制芯片。4.CC逻辑芯片和E-Mark芯片前文中有说道“持PD的设备必须采用CC Logic芯片”、“Electronically Marked Cable: 封装有E-Marker芯片的USB Type-C有源电缆”、“全功能的Type-C电缆封装有E-Marke”等信息,实际比较细心阅读的就可以看出所谓的CC逻辑芯片和E-MARK芯片是不同的:CC逻辑芯片针对的还是设备端;E-MARK针对的是线缆,而且规范中也经常提到“Electronically Marked Cable”其实就是我们所谓的e-mark芯片。E-MARK芯片技术自动识别电子产品所需的电压和电流。****************************************插入知识点**************************************如果TYPE-C接口提供超过5V的电压,或者是超过3A的电流,那么一定需要TYPE-C接口芯片去实现USB PD协议。      如果您的设备使用5V电压,并且不超过3A的电流。而且设备本身只往外供电,或者只接受对方供电,并且供电角色与数据传输角色为默认搭配(即供电方为HOST,用电方为Slave或者device)。那么不需要TYPE-C芯片。      C-C传输线上是否需要用到E-MARK 芯片。这个判断标准是,使用过程中,电流是否会超过3A?如果不超过,则可以不需要。若需要实现Battery Charging协议,这需要E-MARK芯片,这样既能够实现充电,又能够传输数据。****************************************插入知识点**************************************5.雷电接口和Type C二者关系Type C属于接口规格,雷电3则是指接口所支持的虚拟层面的协议,与Type C并不冲突。雷电带宽可达到40Gbps,相较USB 3.1的10Gbps差距高达4倍。雷电1和雷电2使用的接口形态是Mini DisplayPort口(Mini DP口),前两代雷电接口都有应用在苹果Mac系列电脑中.在USB Type C推出之后,Intel就将雷电3的物理接口由以往的mini DP改为USB Type C,性能上也做了更大的提升。传输速度提升到了 40 Gbps. Intel将接口规格基于Type C物理层,并完全能够实现USB Type C的绝大多数功能。所以雷电3同时支持USB3.1;但支持USB 3.1的Type C接口设备不一定支持雷电规范接口。也就是说USB Type-C不一定是雷电3,但是雷电3一定是USB Type-C接口,所有支持雷电3的接口旁都有一个雷电的“闪电”标志,方便区分。6.准确识别Type-C的标示Type-C类别不同的传输速率,支持不同的协议,标识也是不同的。只带有USB标识:表示支持USB2.0,普通的速率480Mb/s;带有USB标识,外加SS字样:表示支持USB3.1,速率5Gb/s;带有USB标识,外加SS字样,右上角多了数字10:表示支持USB3.1,速率10Gb/s;带有USB标识,外加SS字样,右上角多了数字10,右边增加D字样:表示支持USB3.1,速率10Gb/s,支持视频显示;带有USB标识,且有一个黑色的背景:全部支持PD协议,其中5Gb/s的字体为白色,其余均与上文一致!详情参看以下图示。7.参考文献:“http://news.eeworld.com.cn/xfdz/2015/0323/article_40868.html”;《Type-C Specification》;“https://blog.csdn.net/chenzhen1080/article/details/70183163”;《USB Type-C Specification Release 1.3.pdf》“http://www.eepw.com.cn/article/284329.htm” ;“”http://www.tronixin.com/showNews.asp?id=33;“”http://www.chongdiantou.com/wp/archives/37294.html;  ========================================
最新发布
03-08
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值