Detecting USB drive insertion and removal using windows service and c#

C#.NetWindowsWindows ServicesWmi

C# 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();

       }



   

}

}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKb.View Question on Stackoverflow
Solution 1 - C#VitalyBView Answer on Stackoverflow
Solution 2 - C#Phil MinorView Answer on Stackoverflow
Solution 3 - C#SynView Answer on Stackoverflow
Solution 4 - C#Ashkan Mobayen KhiabaniView Answer on Stackoverflow
Solution 5 - C#lzutaoView Answer on Stackoverflow
Solution 6 - C#John ConradView Answer on Stackoverflow
Solution 7 - C#Mike MarshallView Answer on Stackoverflow
Solution 8 - C#dtwk2View Answer on Stackoverflow
Solution 9 - C#Lance ClevelandView Answer on Stackoverflow
Solution 10 - C#Milind MoreyView Answer on Stackoverflow