Skip to content

Instantly share code, notes, and snippets.

@dtwk2
Last active November 15, 2021 16:26
Show Gist options
  • Save dtwk2/42d3aa1d6f42b6b106228c472272b966 to your computer and use it in GitHub Desktop.
Save dtwk2/42d3aa1d6f42b6b106228c472272b966 to your computer and use it in GitHub Desktop.
#nullable enable
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Management;
using System.Reactive.Linq;
using System.Reactive.Subjects;
/// <summary>
/// <a href="https://stackoverflow.com/questions/620144/detecting-usb-drive-insertion-and-removal-using-windows-service-and-c-sharp"/>
/// </summary>
public class UsbDetector
{
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 appDataPath = System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string deviceId = (string)instance.GetPropertyValue("DeviceID");
string serialNr = deviceId.Substring(deviceId.LastIndexOf('\\')).Replace("\\", "");
char? driveLetter = GetDriveLetter(serialNr)?.First();
return new USBDeviceInfo(deviceId, serialNr, driveLetter);
}
public struct USBDeviceInfo
{
public USBDeviceInfo(string deviceId, string SerialNr, char? driveLetter)
{
this.DeviceId = deviceId;
this.SerialNr = SerialNr;
DriveLetter = driveLetter;
}
public string DeviceId { get; }
public string SerialNr { get; }
public char? DriveLetter { get; }
public override string ToString() => $"DeviceId: {DeviceId}; SerialNr: {SerialNr}; DriveLetter: {DriveLetter}";
}
/// <summary>
/// https://stackoverflow.com/questions/31938686/how-to-get-the-drive-letter-of-usb-device-using-wmi
/// </summary>
/// <returns></returns>
static IEnumerable<(string deviceId, string pnpDeviceId, string? driveLetter)> SelectDeviceInformation()
{
foreach (ManagementObject device in SelectDevices())
{
var deviceId = (string)device.GetPropertyValue("DeviceID");
var pnpDeviceId = (string)device.GetPropertyValue("PNPDeviceID");
var driveLetter = (string?)SelectPartitions(device).SelectMany(SelectDisks).Select(disk => disk["Name"]).SingleOrDefault();
yield return (deviceId, pnpDeviceId, driveLetter);
}
static IEnumerable<ManagementObject> SelectDevices() => new ManagementObjectSearcher(
@"SELECT * FROM Win32_DiskDrive WHERE InterfaceType LIKE 'USB%'").Get()
.Cast<ManagementObject>();
static IEnumerable<ManagementObject> SelectPartitions(ManagementObject device) => new ManagementObjectSearcher(
"ASSOCIATORS OF {Win32_DiskDrive.DeviceID=" +
"'" + device.Properties["DeviceID"].Value + "'} " +
"WHERE AssocClass = Win32_DiskDriveToDiskPartition").Get()
.Cast<ManagementObject>();
static IEnumerable<ManagementObject> SelectDisks(ManagementObject partition) => new ManagementObjectSearcher(
"ASSOCIATORS OF {Win32_DiskPartition.DeviceID=" +
"'" + partition["DeviceID"] + "'" +
"} WHERE AssocClass = Win32_LogicalDiskToPartition").Get()
.Cast<ManagementObject>();
}
private static string? GetDriveLetter(string serialNr)
{
return SelectDeviceInformation()
.SingleOrDefault(a =>
a.pnpDeviceId.Remove(a.pnpDeviceId.Length - 2).Substring(a.pnpDeviceId.LastIndexOf('\\')).Replace("\\", "") ==(serialNr)).driveLetter;
}
}
@duchdtran
Copy link

@elmazzun Thank you very much

@elmazzun
Copy link

You are welcome!
I am not a C# expert but you should be able to get something working out of my repo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment