Skip to content

Instantly share code, notes, and snippets.

@jasonbrice
Last active December 15, 2015 00:38
Show Gist options
  • Save jasonbrice/5173849 to your computer and use it in GitHub Desktop.
Save jasonbrice/5173849 to your computer and use it in GitHub Desktop.
Simple class to detect insertion/removal of Human Interface Device (e.g., user sticks a thumb drive into a USB port)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace HID
{
public static class Constants
{
// Windows constants for dealing with HID devices
public const int WM_DEVICECHANGE = 0x0219; // Generic plug & play event
public const int DBT_DEVICEARRIVAL = 0x8000; // Device ready for use
public const int DBT_DEVTYP_VOLUME = 2; // Removable device
public const int DBT_DEVICEREMOVEPENDING = 0x8003; // Device about to be removed
public const int DBT_DEVICEREMOVECOMPLETE = 0x8004; // Device Removed
}
public enum EVENT : int
{
DEVICE_AVAILABLE,
DEVICE_DISCONNECTED,
}
// Serves as a standard header for information related to a device
// event reported through the WM_DEVICECHANGE message.
[StructLayout(LayoutKind.Sequential)]
struct DEV_BROADCAST_HDR
{
public int Size;
public int DeviceType;
public int reserved;
}
// Contains information about a logical volume.
[StructLayout(LayoutKind.Sequential)]
struct DEV_BROADCAST_VOLUME
{
public DEV_BROADCAST_HDR Header;
public int UnitMask;
public int Flags;
}
public class HIDDevice
{
public static event EventHandler<HIDMessageArgs> OnUsbDriveConnect = delegate { };
public static event EventHandler<HIDMessageArgs> OnUsbDriveDisconnect = delegate { };
public static event EventHandler<HIDMessageArgs> OnUsbDriveDisconnectPending = delegate { };
private string driveLetter;
public string DriveLetter { get { return driveLetter; } }
public bool IsAvailable
{
get
{
if (string.IsNullOrEmpty(DriveLetter)) return false;
foreach (string s in Environment.GetLogicalDrives())
{
if (s.Equals(DriveLetter))
{
return true;
}
}
return false;
}
}
public HIDDevice(ref System.Windows.Forms.Message m)
{
DEV_BROADCAST_HDR hdr = new DEV_BROADCAST_HDR();
switch (m.WParam.ToInt32())
{
// at this point the USB device is ready for use.
case Constants.DBT_DEVICEARRIVAL:
// get a handle on the device header
hdr = (DEV_BROADCAST_HDR)(Marshal.PtrToStructure(m.LParam, typeof(HID.DEV_BROADCAST_HDR)));
// is a removable device?
if (hdr.DeviceType == Constants.DBT_DEVTYP_VOLUME)
{
// get a handle on the volume
DEV_BROADCAST_VOLUME vlm = new DEV_BROADCAST_VOLUME();
vlm = (DEV_BROADCAST_VOLUME)Marshal.PtrToStructure(m.LParam, typeof(HID.DEV_BROADCAST_VOLUME));
char drv = GetDriveLetter((ulong)vlm.UnitMask);
this.driveLetter = drv.ToString() + @":\";
// fire an event off to anybody listening
// for new HID messages.
OnUsbDriveConnect(this, new HIDMessageArgs(EVENT.DEVICE_AVAILABLE, drv));
}
else
{
Console.WriteLine("Not a removeable device");
}
break;
// devive about to be removed
case Constants.DBT_DEVICEREMOVEPENDING:
// get a handle on the device header
hdr = (DEV_BROADCAST_HDR)(Marshal.PtrToStructure(m.LParam, typeof(HID.DEV_BROADCAST_HDR)));
// is a removable device?
if (hdr.DeviceType == Constants.DBT_DEVTYP_VOLUME)
{
// get a handle on the volume
DEV_BROADCAST_VOLUME vlm = new DEV_BROADCAST_VOLUME();
vlm = (DEV_BROADCAST_VOLUME)Marshal.PtrToStructure(m.LParam, typeof(HID.DEV_BROADCAST_VOLUME));
char drv = GetDriveLetter((ulong)vlm.UnitMask);
OnUsbDriveDisconnectPending(this, new HIDMessageArgs(EVENT.DEVICE_AVAILABLE, drv));
}
break;
// device removal complete
case Constants.DBT_DEVICEREMOVECOMPLETE:
Console.WriteLine("Device disconnected.");
// fire an event off to anybody listening
// for new UDPMessages.
OnUsbDriveDisconnect(this, new HIDMessageArgs(EVENT.DEVICE_DISCONNECTED));
break;
default:
//Lots of these... safe to ignore
//Console.WriteLine("Unknown action: " + m.WParam.ToInt32());
break;
}
}
// return the drive letter given an unsigned long int
private static char GetDriveLetter(ulong m)
{
int i;
for (i = 0; i < 26; ++i)
{
if ((m & 0x1L) != 0)
break;
m = m >> 1;
}
return (char)(i + 'A');
}
}
// These args will be delivered to any class
// listening for USB attach / detach events.
public class HIDMessageArgs : EventArgs
{
private EVENT _eventType;
private char driveLetter;
public char DriveLetter { get { return driveLetter; } }
public EVENT EventType { get { return _eventType; } }
public HIDMessageArgs(EVENT type)
{
_eventType = type;
}
public HIDMessageArgs(EVENT type, char driveLetter)
{
_eventType = type;
this.driveLetter = driveLetter;
}
}
}
public Form1()
{
InitializeComponent();
HID.HIDDevice.OnUsbDriveConnect += new EventHandler<HID.HIDMessageArgs>(DriveConnected);
HID.HIDDevice.OnUsbDriveDisconnectPending += new EventHandler<HID.HIDMessageArgs>(DriveDisconnectedPending);
HID.HIDDevice.OnUsbDriveDisconnect += new EventHandler<HID.HIDMessageArgs>(DriveDisconnected);
}
// Listen for windows messages
protected override void DefWndProc(ref Message m)
{
try
{
// quite a number of these can be generated during
// the plug & play process...
if (m.Msg == HID.Constants.WM_DEVICECHANGE)
{
HID.HIDDevice device = new HID.HIDDevice(ref m);
}
else
base.DefWndProc(ref m);
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
}
private void DriveConnected(object sender, HID.HIDMessageArgs e)
{
HID.HIDDevice usb = (HID.HIDDevice)sender;
Console.WriteLine("Detected drive " + usb.DriveLetter + " connected");
}
private void DriveDisconnectedPending(object sender, HID.HIDMessageArgs e)
{
HID.HIDDevice usb = (HID.HIDDevice)sender;
Console.WriteLine("Detected drive " + usb.DriveLetter + " disconnect pending");
}
private void DriveDisconnected(object sender, HID.HIDMessageArgs e)
{
HID.HIDDevice usb = (HID.HIDDevice)sender;
Console.WriteLine("Detected drive disconnected");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment