Detecting USB drive insertion and removal using windows service and c#
C#.NetWindowsWindows ServicesWmiC# Problem Overview
Looking into possibility of making an USB distributed application
that will autostart on insertion of an USB stick and shutdown when removing the stick
Will use .Net and C#.
Looking for suggestion how to approach this using C#?
Update: Two possible solutions implementing this as a service.
- override WndProc
or - using WMI query with ManagementEventWatcher
C# Solutions
Solution 1 - C#
You can use WMI, it is easy and it works a lot better than WndProc solution with services.
Here is a simple example:
using System.Management;
ManagementEventWatcher watcher = new ManagementEventWatcher();
WqlEventQuery query = new WqlEventQuery("SELECT * FROM Win32_VolumeChangeEvent WHERE EventType = 2");
watcher.EventArrived += new EventArrivedEventHandler(watcher_EventArrived);
watcher.Query = query;
watcher.Start();
watcher.WaitForNextEvent();
Solution 2 - C#
This works well for me, plus you can find out more information about the device.
using System.Management;
private void DeviceInsertedEvent(object sender, EventArrivedEventArgs e)
{
ManagementBaseObject instance = (ManagementBaseObject)e.NewEvent["TargetInstance"];
foreach (var property in instance.Properties)
{
Console.WriteLine(property.Name + " = " + property.Value);
}
}
private void DeviceRemovedEvent(object sender, EventArrivedEventArgs e)
{
ManagementBaseObject instance = (ManagementBaseObject)e.NewEvent["TargetInstance"];
foreach (var property in instance.Properties)
{
Console.WriteLine(property.Name + " = " + property.Value);
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
WqlEventQuery insertQuery = new WqlEventQuery("SELECT * FROM __InstanceCreationEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_USBHub'");
ManagementEventWatcher insertWatcher = new ManagementEventWatcher(insertQuery);
insertWatcher.EventArrived += new EventArrivedEventHandler(DeviceInsertedEvent);
insertWatcher.Start();
WqlEventQuery removeQuery = new WqlEventQuery("SELECT * FROM __InstanceDeletionEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_USBHub'");
ManagementEventWatcher removeWatcher = new ManagementEventWatcher(removeQuery);
removeWatcher.EventArrived += new EventArrivedEventHandler(DeviceRemovedEvent);
removeWatcher.Start();
// Do something while waiting for events
System.Threading.Thread.Sleep(20000000);
}
Solution 3 - C#
Adding to VitalyB's post.
To raise an event where ANY USB device is inserted, use the following:
var watcher = new ManagementEventWatcher();
var query = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 2");
watcher.EventArrived += new EventArrivedEventHandler(watcher_EventArrived);
watcher.Query = query;
watcher.Start();
This will raise an event whenever a USB device is plugged. It even works with a National Instruments DAQ that I'm trying to auto-detect.
Solution 4 - C#
VitalyB's answer does't cover remove of the device. I changed it a bit to trigger the event both when media is inserted and removed and also code to get the drive letter of the inserted media.
using System;
using System.Management;
namespace MonitorDrives
{
class Program
{
public enum EventType
{
Inserted = 2,
Removed = 3
}
static void Main(string[] args)
{
ManagementEventWatcher watcher = new ManagementEventWatcher();
WqlEventQuery query = new WqlEventQuery("SELECT * FROM Win32_VolumeChangeEvent WHERE EventType = 2 or EventType = 3");
watcher.EventArrived += (s, e) =>
{
string driveName = e.NewEvent.Properties["DriveName"].Value.ToString();
EventType eventType = (EventType)(Convert.ToInt16(e.NewEvent.Properties["EventType"].Value));
string eventName = Enum.GetName(typeof(EventType), eventType);
Console.WriteLine("{0}: {1} {2}", DateTime.Now, driveName, eventName);
};
watcher.Query = query;
watcher.Start();
Console.ReadKey();
}
}
}
Solution 5 - C#
A little bit edit on all above answer:
using System.Management;
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
bgwDriveDetector.DoWork += bgwDriveDetector_DoWork;
bgwDriveDetector.RunWorkerAsync();
}
private void DeviceInsertedEvent(object sender, EventArrivedEventArgs e)
{
string driveName = e.NewEvent.Properties["DriveName"].Value.ToString();
MessageBox.Show(driveName + " inserted");
}
private void DeviceRemovedEvent(object sender, EventArrivedEventArgs e)
{
string driveName = e.NewEvent.Properties["DriveName"].Value.ToString();
MessageBox.Show(driveName + " removed");
}
void bgwDriveDetector_DoWork(object sender, DoWorkEventArgs e)
{
var insertQuery = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 2");
var insertWatcher = new ManagementEventWatcher(insertQuery);
insertWatcher.EventArrived += DeviceInsertedEvent;
insertWatcher.Start();
var removeQuery = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 3");
var removeWatcher = new ManagementEventWatcher(removeQuery);
removeWatcher.EventArrived += DeviceRemovedEvent;
removeWatcher.Start();
}
}
Solution 6 - C#
You can also use WMI to detect insertion events. It's a little bit more complicated than monitoring for WM_CHANGEDEVICE messages, but it does not require a window handle which may be useful if you are running in the background as a service.
Solution 7 - C#
Try WM_CHANGEDEVICE handling.
Solution 8 - C#
My complete answer can be found here as a gist
I found the answer to determining the drive letter from the serial # from this question/answer https://stackoverflow.com/questions/31938686/how-to-get-the-drive-letter-of-usb-device-using-wmi/60548941#60548941
And I modified Phil Minor's code to make it reactive:
public class UsbDetector : IUsbDetector
{
private const string Query = "SELECT * FROM {0} WITHIN 2 WHERE TargetInstance ISA 'Win32_USBHub'";
private const string CreationEvent = "__InstanceCreationEvent";
private const string DeletionEvent = "__InstanceDeletionEvent";
private const int ReplayNumber = 1;
private readonly Subject<USBDeviceInfo> adds = new Subject<USBDeviceInfo>();
private readonly Subject<USBDeviceInfo> removes = new Subject<USBDeviceInfo>();
public UsbDetector()
{
var bgwDriveDetector = new BackgroundWorker();
bgwDriveDetector.DoWork += DoWork;
bgwDriveDetector.RunWorkerAsync();
}
public IObservable<USBDeviceInfo> Adds => adds.AsObservable();
public IObservable<USBDeviceInfo> Removes => removes.AsObservable();
private void DoWork(object sender, DoWorkEventArgs e)
{
SubscribeToEvent(CreationEvent, adds);
SubscribeToEvent(DeletionEvent, removes);
}
private static void SubscribeToEvent(string eventType, IObserver<USBDeviceInfo> observer)
{
WqlEventQuery wqlEventQuery = new WqlEventQuery(string.Format(Query, eventType));
ManagementEventWatcher insertWatcher = new ManagementEventWatcher(wqlEventQuery);
var observable = Observable.FromEventPattern<EventArrivedEventHandler, EventArrivedEventArgs>(
h => insertWatcher.EventArrived += h,
h => insertWatcher.EventArrived -= h).Replay(ReplayNumber);
observable.Connect();
observable.Select(a => a.EventArgs).Select(MapEventArgs).Subscribe(observer);
insertWatcher.Start();
}
private static USBDeviceInfo MapEventArgs(EventArrivedEventArgs e)
{
ManagementBaseObject instance = (ManagementBaseObject)e.NewEvent["TargetInstance"];
string deviceId = (string)instance.GetPropertyValue("DeviceID");
string serialNr = deviceId.Substring(deviceId.LastIndexOf('\\')).Replace("\\", "");
char driveLetter = GetDriveLetter(serialNr).First();
return new USBDeviceInfo(deviceId, serialNr, driveLetter);
}
Solution 9 - C#
Here is what we did with C# .Net 4.0 under a WPF app. We are still searching for an answer to "how to tell WHICH device type was inserted/removed", but this is a start:
using System.Windows.Interop;
...
public partial class MainWindow : Window
{
...
public MainWindow()
{
...
}
//============================================================
// WINDOWS MESSAGE HANDLERS
//
private const int WM_DEVICECHANGE = 0x0219; // int = 537
private const int DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = 0x00000004;
/// <summary>
///
/// </summary>
/// <param name="e"></param>
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
source.AddHook(WndProc);
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_DEVICECHANGE)
{
ReadDongleHeader();
}
return IntPtr.Zero;
}
}
Solution 10 - C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Management;
using System.ComponentModel;
namespace ConsoleApplication4
{
public class usbState
{
public usbState()
{
}
private void DeviceInsertedEvent(object sender, EventArrivedEventArgs e)
{
ManagementBaseObject instance = (ManagementBaseObject)e.NewEvent["TargetInstance"];
foreach (var property in instance.Properties)
{
Console.WriteLine(property.Name + " = " + property.Value);
}
}
private void DeviceRemovedEvent(object sender, EventArrivedEventArgs e)
{
ManagementBaseObject instance = (ManagementBaseObject)e.NewEvent["TargetInstance"];
foreach (var property in instance.Properties)
{
Console.WriteLine(property.Name + " = " + property.Value);
}
}
public void bgwDriveDetector_DoWork(object sender, DoWorkEventArgs e)
{
WqlEventQuery insertQuery = new WqlEventQuery("SELECT * FROM __InstanceCreationEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_USBHub'");
ManagementEventWatcher insertWatcher = new ManagementEventWatcher(insertQuery);
insertWatcher.EventArrived += new EventArrivedEventHandler(DeviceInsertedEvent);
insertWatcher.Start();
WqlEventQuery removeQuery = new WqlEventQuery("SELECT * FROM __InstanceDeletionEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_USBHub'");
ManagementEventWatcher removeWatcher = new ManagementEventWatcher(removeQuery);
removeWatcher.EventArrived += new EventArrivedEventHandler(DeviceRemovedEvent);
removeWatcher.Start();
}
}
class Class1
{
private static void Main(string[] args)
{
usbState usb= new usbState();
BackgroundWorker bgwDriveDetector = new BackgroundWorker();
bgwDriveDetector.DoWork += usb.bgwDriveDetector_DoWork;
bgwDriveDetector.RunWorkerAsync();
bgwDriveDetector.WorkerReportsProgress = true;
bgwDriveDetector.WorkerSupportsCancellation = true;
// System.Threading.Thread.Sleep(100000);
Console.ReadKey();
}
}
}