Detecting USB Devices Using C#

If you're building an IoT (Internet of Things)-based gadget, one thing you might want to know about is when USB devices are attached to or removed from your platform.

Specifically, as we'll see in this post, you might be interested to know when a USB Thumb drive or other USB-based storage media is plugged in, or removed.

To achieve this, there are a few different techniques you can use. If, for example, you want to get the USB device's GUID and Vendor & Product IDs, you'll want to use the WMI (Windows Management Interface) to do this. Typically, WMI is quite complicated and very, very code heavy, so we'll leave this one for a future post.

In this post, we're going to look at a slightly easier method of performing this task by using the standard Windows message pump.

Because we're going to be overriding the message pump, this means you can't do this in a console application (there are ways, however), so we'll be creating a Windows Forms program to demonstrate the technique. I should imagine that it's also possible to do this in WPF too, but I don't know how to override the message pump in a WPF app. (If you do, feel free to leave a note in the comments.)

Let's Write Some Code….

Start up Visual Studio and create a standard Windows Forms application.

Press F7 to go into the code editor, and add the following to your Form1 class:

protected override void WndProc(ref Message m)
{
   base.WndProc(ref m);
}

By doing this, you're overriding the normal "Windows Proc" that handles all the messages for your form. You MUST put the base call in first; if you don't, none of the regular Windows messages will be acted upon, essentially freezing your form.

Once you have this in place, press F5 run your app and make sure the blank form behaves as normal. When you're satisfied that it does, stop the app and add a list box to the form as the Figure 1 shows:

USB1
Figure 1: Our form with 'listBox1' on it

Switch back to code view for your form.

The next thing we need to do is to handle a custom message that gets sent to our application when a device is added or removed. This message is called "WM_DEVICECHANGED" and has the value 0x219 (in hexadecimal).

Alter our overridden message handler, so that it now looks like this:

protected override void WndProc(ref Message m)
{
   base.WndProc(ref m);

   switch(m.Msg)
   {
      case WM_DEVICECHANGE:
      switch((int)m.WParam)
      {
      }
      break;
   }
}

This now allows us to have a point in our handler when every time we receive one of these events, our code will be called.

In C#, you'll also need to add a

private const int WM_DEVICECHANGE = 0x219;

somewhere near the top of the class, most likely just after the opening 'public partial class Form1' line.

Once we know we have a WM_DEVICECHANGE event, we then need to examine the m.WParam value because this will tell us the actual device action that has happened. If you place a break point on this code, you'll find that you get way more than one notification for addition and one for removal as you might expect. In fact, you'll get a lot of events with the value 7 in WParam, which you can safely ignore.

The only two message codes you need to be concerned with are "DBT_DEVICEARRIVAL" and "DBT_DEVICEREMOVECOMPLETE". Just underneath the "WM_DEVICECHANGE" const you defined a moment ago, add the following three constants:

private const int DBT_DEVICEARRIVAL = 0x8000;
private const int DBT_DEVICEREMOVECOMPLETE = 0x8004;
private const int DBT_DEVTYP_VOLUME = 0x00000002;

I'll explain what the third one is for in just a moment.

Inside the "WParam" switch, add the following:

case DBT_DEVICEARRIVAL:
   listBox1.Items.Add("New Device Arrived");
   break;

case DBT_DEVICEREMOVECOMPLETE:
   listBox1.Items.Add("Device Removed");
   break;

If you run your application now, and add/remove a thumb drive or other removable media to your PC, you should see messages telling you what's happening appearing on your form.

USB2
Figure 2: Our app now knows when a device is plugged in and removed.

At this point, if all you wanted to do was know something had been plugged in, that would be great. Our application, as it stands, will tell you about every device addition and removal, no matter what type of device it is.

If we want to know only about storage media, we need to do a further check for "DBT_DEVTYPE_VOLUME" which is the second parameter of the "DEV_BROADCAST_HDR" structure, pointed to by 'm.LParam' in your message handler.

You can think of "DEV_BROADCAST_HDR" as being a super class to a number of different structures, one of which is "DBT_DEVTYP_VOLUME"; this is the one we are interested in.

To check the type of the structure, the second 32-bit integer in the memory pointed to by LParam will be a value indicating which of the five types (all of which are documented here) the DBT_DEVTYPE_VOLUME value of 2 we defined a moment ago, is the one we're interested in finding.

Once we know we have a value of 2, we then need to copy the pointer contents into a C# class that mirrors the native Windows structure, so we can read the values and act upon them.

Add a new class to your project and call it "DevBroadcastVolume.cs"; then, make sure it has the following code in it:

using System;
using System.Runtime.InteropServices;

namespace UsbDriveDetector
{
   [StructLayout(LayoutKind.Sequential)]
   public struct DevBroadcastVolume
   {
      public int Size;
      public int DeviceType;
      public int Reserved;
      public int Mask;
      public Int16 Flags;
   }
}

Switch back to the code you have in Form1, and in your "DBT_DEVICEARRIVAL" case, add the following just after the listBox1 item add call:

int devType = Marshal.ReadInt32(m.LParam, 4);
if(devType == DBT_DEVTYP_VOLUME)
{
   DevBroadcastVolume vol;
   vol = (DevBroadcastVolume) Marshal.PtrToStructure(m.LParam,
      typeof (DevBroadcastVolume));
   listBox1.Items.Add("Mask is " + vol.Mask);
}

If you run your app now, and watch the form, you'll see an extra line has been added; it contains the device mask:

USB3
Figure 3: Our app now displays the inserted device mask, but ONLY if it's a storage volume.

In Figure 3, you can see the mask value is 131072, which if you convert to binary, gives you the following value:

00000000000000100000000000000000

Starting at the left, and giving each column of the number a single letter of the alphabet, you should get

      ZYXWVUTSRQPONMLKJIHGFEDCBA
00000000000000100000000000000000

You'll see that the '1' appears under the 'R' column, and if I look in my Explorer view:

USB4
Figure 4: Explorer view showing inserted drive

You'll see the key chain drive I plugged in was mounted as Drive R.

For reference, the full code for your Form1 class should look something like the following:

using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace UsbDriveDetector
{
   public partial class Form1 : Form
   {
      private const int WM_DEVICECHANGE = 0x219;
      private const int DBT_DEVICEARRIVAL = 0x8000;
      private const int DBT_DEVICEREMOVECOMPLETE = 0x8004;
      private const int DBT_DEVTYP_VOLUME = 0x00000002;

      public Form1()
      {
         InitializeComponent();
      }

      protected override void WndProc(ref Message m)
      {
         base.WndProc(ref m);

         switch(m.Msg)
         {
            case WM_DEVICECHANGE:
               switch((int)m.WParam)
               {
                  case DBT_DEVICEARRIVAL:
                     listBox1.Items.Add("New Device Arrived");

                     int devType = Marshal.ReadInt32(m.LParam, 4);
                     if(devType == DBT_DEVTYP_VOLUME)
                     {
                        DevBroadcastVolume vol;
                        vol = (DevBroadcastVolume)
                           Marshal.PtrToStructure(m.LParam,
                           typeof (DevBroadcastVolume));
                        listBox1.Items.Add("Mask is " + vol.Mask);
                     }

                  break;

               case DBT_DEVICEREMOVECOMPLETE:
                  listBox1.Items.Add("Device Removed");
                  break;

            }
            break;
         }

      }
   }
}

And that's all there is to this. What you do with the event data is entirely up to you, and if you look on MSDN at the different structures, you'll see you easily can get the same information for things like COM ports, modems, and other USB-based communication devices.

Got a tricky .NET problem that's nagging you? Drop me a note in the comments below, or come hunt me down on Twitter as @shawty_ds and I'll see if I can write it up in a post.



About the Author

Peter Shaw

As an early adopter of IT back in the late 1970s to early 1980s, I started out with a humble little 1KB Sinclair ZX81 home computer. Within a very short space of time, this small 1KB machine became a 16KB Tandy TRS-80, followed by an Acorn Electron and, eventually, after going through many other different machines, a 4MB, ARM-powered Acorn A5000. After leaving school and getting involved with DOS-based PCs, I went on to train in many different disciplines in the computer networking and communications industries. After returning to university in the mid-1990s and gaining a Bachelor of Science in Computing for Industry, I now run my own consulting business in the northeast of England called Digital Solutions Computer Software, Ltd. I advise clients at both a hardware and software level in many different IT disciplines, covering a wide range of domain-specific knowledge—from mobile communications and networks right through to geographic information systems and banking and finance.

Related Articles

Comments

  • System Analyst

    Posted by Mohammed Alzahrani on 06/19/2016 04:03pm

    Thank you Peter for the great solution. It is working fine with me when i connect USB flash memory, However it doesn't detect my Samsung Galaxy S6 when I connect it using USB cable. Is there a way to do it? Regards

    • Try this:

      Posted by replier on 06/28/2016 03:33am

      Put a breakpoint on line 31 and check the value of devType. Then go here: https://msdn.microsoft.com/en-us/library/aa363246(v=vs.85).aspx and in 'Members' section you can see a list(table) with values and meanings. Now compare the value of devType with the values you see on the left of the list(table). For example if you have 3 check for 0x00000003). Then see the 'meaning' of the value you have and then create a structure (like in the example) that fits the needs of the structure that you see in the 'meaning' (for example: for devType 3 the structure is 'DEV_BROADCAST_PORT').

      Reply
    Reply
  • CS student

    Posted by David Hoff on 06/02/2016 07:56am

    This is a great introductory article on how to communicate with a USB device from within C#. Other articles are very complicated or assume you already know a whole lot. Do you know of a follow-on article that gives more information about how to access a file from a phone sdcard connected by USB? Thanks again.

    Reply
  • detecting usb devices and mounted letter

    Posted by Herby on 04/23/2016 06:06am

    i had an alternative method bt this one was very easy and reliable and u can get the drive letterwith this small algorithm i cameup with int mask = vol.Mask; String binaryMask = Convert.ToString(vol.Mask,2); int str = binaryMask.Length - binaryMask.IndexOf('1') -1; char A = (char)('A' + str);

    • Coding Error

      Posted by Hassnain Mehmood on 05/22/2016 04:30am

      Upper code is not working means auther code please guide me for any Usb device....!

      Reply
    Reply
  • Reading/Writing data to detected USB

    Posted by Boda on 02/20/2016 09:04am

    Frist of all, thanks for sharing = ) .. Now I need to play around files in this USB, reading and writing stuff, any help in that :D ?

    Reply
  • Reading/Writing data to detected USB

    Posted by Boda on 02/20/2016 09:01am

    I'm really glad that I've read that post, thanks for sharing with the world :) .. Now I need the play around with the files inside that USB Drive (Reading, Writing, etc..), any help about that?

    Reply
  • Sending SMS from windows application in C# using GSM Modem

    Posted by Justin on 11/24/2015 01:19am

    Am developing windows C# application which i want to send sms using GSM modem

    Reply
  • Sending SMS from windows application in C# using GSM Modem

    Posted by Justin on 11/24/2015 01:16am

    Am developing Windows Application using C# which will sent sms to clients using a GSM Modem, any assistance will be of help

    Reply
  • Comments

    Posted by A. F. M. Golam Kibria on 11/09/2015 08:18pm

    working fine.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date