Last active
November 15, 2021 16:26
-
-
Save dtwk2/42d3aa1d6f42b6b106228c472272b966 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} | |
} |
How to use it? Sorry, I am newbie
@duchdtran here's a repository I made as minimal working example.
It is a Visual Studio solution.
@elmazzun Thank you very much
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
@elmazzun - thanks -replaced the instances of Single with SingleOrDefault