Get the Serial Number of Usb Storage Devices in .Net Core 2.1

Get the serial number of USB storage devices in .Net Core 2.1

This Class performs a series of queries on the WMI Win32_DiskDrive class and its associators: Win32_DiskDriveToDiskPartition and CIM_LogicalDiskBasedOnPartition, to retrieve informations on the active USB Drives on a System (local or remote).

It might seem redundant (probably because it is), since you just asked for the USB Drives Serial Number. But, you never know what you will need next, and it could be useful to someone else.

It requires Microsoft .Net System.Management 4.5 for .Net Core 2.1 (NuGet Package)

Can be easily found and installed using the Visual Studio NuGet Package Manager.

About Linux support, read here:

Windows Management Instrumentation Now A Formal Bus With Linux 4.13

Also, keep an eye out for Windows Compatibility Pack for .NET Core.

New cross-platform assemblies are constantly added and updated.

The main class implements all the required functionalities and it has quite a simple structure.

The WMI queries use the Associator syntax, a method to correlate WMI class objects related to each other.

The Class properties meaning is self-explanatory.

Can be instantiated this way:

SystemUSBDrives systemUSBDrives = new SystemUSBDrives("[Computer Name]");

When [Computer Name] is null or empty, it uses the Local Machine name.

To get the List of USB Devices and their properties, call the GetUSBDrivesInfo() method:

var USBDrivesEnum = systemUSBDrives.GetUSBDrivesInfo([UserName], [Password], [Domain]);

[UserName], [Password], [Domain] are used to connect to a NT Domain.

These parameters, if not needed, can be null or an empty string.

Sample class instantiation and function call (Local Machine, no authentication):

SystemUSBDrives systemUSBDrives = new SystemUSBDrives(null);
var USBDrivesEnum = systemUSBDrives.GetUSBDrivesInfo(null, null, null);

Tested on:

Visual Studio Pro 15.7.6 - 15.9.35

.Net Core 2.1 / .Net Framework 4.8

C# 6.0 -> 7.3

.Net System.Management 4.5

using System.Management;

public class SystemUSBDrives
{
string m_ComputerName = string.Empty;
public SystemUSBDrives(string ComputerName) {
this.m_ComputerName = string.IsNullOrEmpty(ComputerName)
? Environment.MachineName
: ComputerName;
}

private static EnumerationOptions GetEnumerationOptions(bool DeepScan)
{
var mOptions = new EnumerationOptions()
{
Rewindable = false, //Forward only query => no caching
ReturnImmediately = true, //Pseudo-async result
DirectRead = true,
EnumerateDeep = DeepScan
};
return mOptions;
}

private static ConnectionOptions GetConnectionOptions() => GetConnectionOptions("", "", "");

private static ConnectionOptions GetConnectionOptions(string userName, string password, string domain)
{
var connOptions = new ConnectionOptions()
{
EnablePrivileges = true,
Timeout = ManagementOptions.InfiniteTimeout,
Authentication = AuthenticationLevel.PacketPrivacy,
Impersonation = ImpersonationLevel.Impersonate,
Username = userName,
Password = password,
Authority = !string.IsNullOrEmpty(Domain) ? $"NTLMDOMAIN:{domain}" : string.Empty //Authority = "NTLMDOMAIN:[domain]"
};
return connOptions;
}

public List<USBDriveInfo> GetUSBDrivesInfo(string userName, string password, string domain)
{
var wmiQueryResult = new List<USBDriveInfo>();
ConnectionOptions connOptions = GetConnectionOptions(userName, password, domain);
EnumerationOptions mOptions = GetEnumerationOptions(false);
var mScope = new ManagementScope($@"\\{this.m_ComputerName}\root\CIMV2", connOptions);
var selQuery = new SelectQuery("SELECT * FROM Win32_DiskDrive WHERE InterfaceType='USB'");
mScope.Connect();

using (var moSearcher = new ManagementObjectSearcher(mScope, selQuery, mOptions)) {
foreach (ManagementObject moDiskDrive in moSearcher.Get()) {
var usbInfo = new USBDriveInfo();
usbInfo.GetDiskDriveInfo(moDiskDrive);

var relQuery = new RelatedObjectQuery(
$"Associators of {{Win32_DiskDrive.DeviceID='{moDiskDrive.Properties["DeviceID"].Value}'}} " +
$"WHERE AssocClass = Win32_DiskDriveToDiskPartition");
using (var moAssocPart = new ManagementObjectSearcher(mScope, relQuery, mOptions)) {
foreach (ManagementObject moAssocPartition in moAssocPart.Get()) {

usbInfo.GetDiskPartitionInfo(moAssocPartition);
relQuery = new RelatedObjectQuery(
$"Associators of {{Win32_DiskPartition.DeviceID='{moAssocPartition.Properties["DeviceID"].Value}'}} " +
$"WHERE AssocClass = CIM_LogicalDiskBasedOnPartition");

using (var moLogDisk = new ManagementObjectSearcher(mScope, relQuery, mOptions)) {
foreach (ManagementObject moLogDiskEnu in moLogDisk.Get()) {
usbInfo.GetLogicalDiskInfo(moLogDiskEnu);
moLogDiskEnu.Dispose();
}
}
moAssocPartition.Dispose();
}
wmiQueryResult.Add(usbInfo);
}
moDiskDrive.Dispose();
}
return wmiQueryResult;
}
} //GetUSBDrivesInfo()

public class USBDriveInfo
{
private int m_PartionsCount = 0;
public USBDriveInfo() => this.Partitions = new List<Partition>(1);
public string Caption { get; private set; }
public string DeviceID { get; private set; }
public string FirmwareRevision { get; private set; }
public ulong FreeSpace { get; private set; }
public string InterfaceType { get; private set; }
public bool MediaLoaded { get; private set; }
public string MediaType { get; private set; }
public string Model { get; private set; }
public uint NumberOfPartitions { get; private set; }
public List<Partition> Partitions { get; private set; }
public string PNPDeviceID { get; private set; }
public string SerialNumber { get; private set; }
public ulong Size { get; private set; }
public string Status { get; private set; }
public ulong TotalCylinders { get; private set; }
public uint TotalHeads { get; private set; }
public ulong TotalSectors { get; private set; }
public ulong TotalTracks { get; private set; }
public uint TracksPerCylinder { get; private set; }

public class Partition
{
public Partition() => this.LogicalDisks = new List<LogicalDisk>();
public bool Bootable { get; internal set; }
public bool BootPartition { get; internal set; }
public uint DiskIndex { get; internal set; }
public List<LogicalDisk> LogicalDisks { get; internal set; }
public ulong PartitionBlockSize { get; internal set; }
public ulong PartitionNumberOfBlocks { get; internal set; }
public ulong PartitionStartingOffset { get; internal set; }
public bool PrimaryPartition { get; internal set; }
}

public class LogicalDisk
{
public ulong FreeSpace { get; internal set; }
public string FileSystem { get; internal set; }
public string LogicalDiskVolume { get; internal set; }
public bool SupportsDiskQuotas { get; internal set; }
public string VolumeName { get; internal set; }
public string VolumeSerialNumber { get; internal set; }
}

internal void GetDiskDriveInfo(ManagementObject DiskDrive)
{
this.Caption = DiskDrive.GetPropertyValue("Caption")?.ToString();
this.DeviceID = DiskDrive["DeviceID"]?.ToString();
this.FirmwareRevision = DiskDrive["FirmwareRevision"]?.ToString();
this.InterfaceType = DiskDrive["InterfaceType"]?.ToString();
this.MediaLoaded = (bool?)DiskDrive["MediaLoaded"] ?? false;
this.MediaType = DiskDrive["MediaType"]?.ToString();
this.Model = DiskDrive["Model"]?.ToString();
this.NumberOfPartitions = (uint?)DiskDrive["Partitions"] ?? 0;
this.PNPDeviceID = DiskDrive["PNPDeviceID"]?.ToString();
this.SerialNumber = DiskDrive["SerialNumber"]?.ToString();
this.Size = (ulong?)DiskDrive["Size"] ?? 0L;
this.Status = DiskDrive["Status"]?.ToString();
this.TotalCylinders = (ulong?)DiskDrive["TotalCylinders"] ?? 0;
this.TotalHeads = (uint?)DiskDrive["TotalHeads"] ?? 0U;
this.TotalSectors = (ulong?)DiskDrive["TotalSectors"] ?? 0;
this.TotalTracks = (ulong?)DiskDrive["TotalTracks"] ?? 0;
this.TracksPerCylinder = (uint?)DiskDrive["TracksPerCylinder"] ?? 0;
}

internal void GetDiskPartitionInfo(ManagementObject Partitions)
{
m_PartionsCount += 1;
this.Partitions.Add(new Partition()
{
DiskIndex = (uint?)Partitions["DiskIndex"] ?? 0,
PartitionBlockSize = (ulong?)Partitions["BlockSize"] ?? 0,
Bootable = (bool?)Partitions["Bootable"] ?? false,
BootPartition = (bool?)Partitions["BootPartition"] ?? false,
PartitionNumberOfBlocks = (ulong?)Partitions["NumberOfBlocks"] ?? 0,
PrimaryPartition = (bool?)Partitions["PrimaryPartition"] ?? false,
PartitionStartingOffset = (ulong?)Partitions["StartingOffset"] ?? 0
});
}

internal void GetLogicalDiskInfo(ManagementObject LogicalDisk)
{
if (m_PartionsCount == 0) return;
this.Partitions[m_PartionsCount - 1].LogicalDisks.Add(new LogicalDisk()
{
FileSystem = LogicalDisk["FileSystem"]?.ToString(),
FreeSpace = (ulong?)LogicalDisk["FreeSpace"] ?? 0,
LogicalDiskVolume = LogicalDisk["DeviceID"]?.ToString(),
SupportsDiskQuotas = (bool?)LogicalDisk["SupportsDiskQuotas"] ?? false,
VolumeName = LogicalDisk["VolumeName"]?.ToString(),
VolumeSerialNumber = LogicalDisk["VolumeSerialNumber"]?.ToString()
});
//Linq's Sum() does not sum ulong(s)
foreach(Partition p in this.Partitions)
{
foreach (LogicalDisk ld in p.LogicalDisks) {
this.FreeSpace += ld.FreeSpace;
}
}
}
}
} // SystemUSBDrives

How do I get information about recently connected USB device?

The Win32_DeviceChangeEvent reports just the type of event that occurred and the Time of the event (uint64, representing 100-nanosecond intervals after January 1, 1601, UTC). No that much useful if you also want to know what arrived or was removed.

I suggest to use instead the WqlEventQuery class, setting its EventClassName to __InstanceOperationEvent.

This system class provides a TargetInstance property the can be cast to a ManagementBaseObject, the full management object that also provides base information on the Device that generated the event.

This information includes (besides the friendly name of the Device) the PNPDeviceID, which can be used to build other queries to further inspect the Device referenced.

The WqlEventQuery's Condition property can be set here to TargetInstance ISA 'Win32_DiskDrive'.

It can be set to any other Win32_ class of interest.

Setup the event listener (local machine):

(The event handler is called DeviceChangedEvent)

var query = new WqlEventQuery() {
EventClassName = "__InstanceOperationEvent",
WithinInterval = new TimeSpan(0, 0, 3),
Condition = @"TargetInstance ISA 'Win32_DiskDrive'"
};

var scope = new ManagementScope("root\\CIMV2");
using (var moWatcher = new ManagementEventWatcher(scope, query))
{
moWatcher.Options.Timeout = ManagementOptions.InfiniteTimeout;
moWatcher.EventArrived += new EventArrivedEventHandler(DeviceChangedEvent);
moWatcher.Start();
}

The event handler receives, in e.NewEvent.Properties["TargetInstance"], the Management Object representing a Win32_DiskDrive class.

See the Docs about the properties directly available here.

The __InstanceOperationEvent derived classes of interest, reported by the e.NewEvent.ClassPath.ClassName, can be:

__InstanceCreationEvent: A new Device arrival has been detected.

__InstanceDeletionEvent: A Device removal has been detected.

__InstanceModificationEvent: An existing device has been modified in some way.

Note that the event is raised in a secondary thread, we need to BeginInvoke() the UI thread to update the UI with the new information.

▶ You should avoid Invoke() here, because it's synchronous: it will block the handler until the method completes. Also, in this context, a dead-lock is not exactly a remote possibility.

See here: Get serial number of USB storage device for a class that provides most of the information available about a device (the information is filtered to show USB devices only, but the filter can be removed).

private void DeviceChangedEvent(object sender, EventArrivedEventArgs e)
{
using (var moBase = (ManagementBaseObject)e.NewEvent.Properties["TargetInstance"].Value)
{
string oInterfaceType = moBase?.Properties["InterfaceType"]?.Value.ToString();
string devicePNPId = moBase?.Properties["PNPDeviceID"]?.Value.ToString();
string deviceDescription = moBase?.Properties["Caption"]?.Value.ToString();
string eventMessage = $"{oInterfaceType}: {deviceDescription} ";

switch (e.NewEvent.ClassPath.ClassName)
{
case "__InstanceDeletionEvent":
eventMessage += " removed";
BeginInvoke(new Action(() => UpdateUI(eventMessage)));
break;
case "__InstanceCreationEvent":
eventMessage += "inserted";
BeginInvoke(new Action(() => UpdateUI(eventMessage)));
break;
case "__InstanceModificationEvent":
default:
Console.WriteLine(e.NewEvent.ClassPath.ClassName);
break;
}
}
}

private void UpdateUI(string message)
{
//Update the UI controls with information provided by the event
}

Detecting USB drive insertion and removal using windows service and 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();

C# USB flash PNPDeviceID different on some systems

I ended up removing ParentIdPrefix from the string and it works well for my scenario:

public static string RemoveParentIdPrefix(string pnpDeviceId)
{
int iSplit = pnpDeviceId.LastIndexOf("\\", StringComparison.InvariantCulture);
string part1 = pnpDeviceId.Substring(0, iSplit);
string part2 = pnpDeviceId.Substring(iSplit);
int ampersandCount = 0;
for (int i = part2.Length - 1; i >= 0; i--)
{
if (part2[i] == '&')
{
ampersandCount++;
}

if (ampersandCount == 2)
{
part2 = part2.Substring(i + 1);
break;
}
}
return part1 + "\\" + part2;
}


Related Topics



Leave a reply



Submit