-
Notifications
You must be signed in to change notification settings - Fork 119
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add new WMI Light component and associated extension methods
- Created `WindowsDeviceIdBuilderExtensions.cs` to add extension methods for adding device components based on WMI Light. - Implemented `WmiLightDeviceIdComponent` to retrieve data from a WMI class using WMI Light. - Implemented `WmiLightMacAddressDeviceIdComponent` for retrieving MAC addresses using WMI Light, with options to exclude wireless and non-physical adapters. - Implemented `WmiLightSystemDriveSerialNumberDeviceIdComponent` to retrieve the system drive's serial number using WMI Light. - Updated `_InternalsVisibleTo.cs` to include `DeviceId.Windows.WmiLight` in the `InternalsVisibleTo` attributes. - Added performance tests for `WmiLightSystemDriveSerialNumberDeviceIdComponent` in `WmiAndMmiDriveSerialNumberPerfTests.cs`.
- Loading branch information
1 parent
4e5e503
commit 5a78ae3
Showing
9 changed files
with
380 additions
and
0 deletions.
There are no files selected for viewing
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
50 changes: 50 additions & 0 deletions
50
src/DeviceId.Windows.WmiLight/Components/WmiLightDeviceIdComponent.cs
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
using System.Collections.Generic; | ||
using WmiLight; | ||
|
||
namespace DeviceId.Windows.WmiLight.Components; | ||
|
||
/// <summary> | ||
/// An implementation of <see cref="IDeviceIdComponent"/> that retrieves data from a WMI class. | ||
/// </summary> | ||
/// <param name="className">The class name.</param> | ||
/// <param name="propertyName">The property name.</param> | ||
public class WmiLightDeviceIdComponent(string className, string propertyName) : IDeviceIdComponent | ||
{ | ||
/// <summary> | ||
/// Gets the component value. | ||
/// </summary> | ||
/// <returns>The component value.</returns> | ||
public string GetValue() | ||
{ | ||
var values = new List<string>(); | ||
|
||
try | ||
{ | ||
using var wmiConnection = new WmiConnection(); | ||
foreach (var wmiObject in wmiConnection.CreateQuery($"SELECT * FROM {className}")) | ||
{ | ||
try | ||
{ | ||
if (wmiObject[propertyName] is string value) | ||
{ | ||
values.Add(value); | ||
} | ||
} | ||
finally | ||
{ | ||
wmiObject.Dispose(); | ||
} | ||
} | ||
} | ||
catch | ||
{ | ||
// Ignore exceptions | ||
} | ||
|
||
values.Sort(); | ||
|
||
return values.Count > 0 | ||
? string.Join(",", values.ToArray()) | ||
: null; | ||
} | ||
} |
149 changes: 149 additions & 0 deletions
149
src/DeviceId.Windows.WmiLight/Components/WmiLightMacAddressDeviceIdComponent.cs
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
using System.Collections.Generic; | ||
using WmiLight; | ||
using DeviceId.Components; | ||
using DeviceId.Internal; | ||
|
||
namespace DeviceId.Windows.WmiLight.Components; | ||
|
||
/// <summary> | ||
/// An implementation of <see cref="IDeviceIdComponent"/> that uses the MAC Address of the PC. | ||
/// This improves upon the basic <see cref="MacAddressDeviceIdComponent"/> by using WMI | ||
/// to get better information from either MSFT_NetAdapter or Win32_NetworkAdapter. | ||
/// </summary> | ||
/// <param name="excludeWireless">A value determining whether wireless devices should be excluded.</param> | ||
/// <param name="excludeNonPhysical">A value determining whether non-physical devices should be excluded.</param> | ||
public class WmiLightMacAddressDeviceIdComponent(bool excludeWireless, bool excludeNonPhysical) : IDeviceIdComponent | ||
{ | ||
/// <summary> | ||
/// Gets the component value. | ||
/// </summary> | ||
/// <returns>The component value.</returns> | ||
public string GetValue() | ||
{ | ||
// First, try to get a value using MSFT_NetAdapter: | ||
try | ||
{ | ||
return GetValueUsingMsftNetAdapter(excludeWireless, excludeNonPhysical); | ||
} | ||
catch { } | ||
|
||
// Next, try using Win32_NetworkAdapter: | ||
try | ||
{ | ||
return GetValueUsingWin32NetworkAdapter(excludeWireless, excludeNonPhysical); | ||
} | ||
catch { } | ||
|
||
// Finally, try the fallback component: | ||
var fallback = new MacAddressDeviceIdComponent(excludeWireless); | ||
return fallback.GetValue(); | ||
} | ||
|
||
/// <summary> | ||
/// Gets the component value using MSFT_NetAdapter. | ||
/// </summary> | ||
/// <param name="excludeWireless">A value determining whether wireless devices should be excluded.</param> | ||
/// <param name="excludeNonPhysical">A value determining whether non-physical devices should be excluded.</param> | ||
/// <returns>The component value.</returns> | ||
private static string GetValueUsingMsftNetAdapter(bool excludeWireless, bool excludeNonPhysical) | ||
{ | ||
var values = new List<string>(); | ||
|
||
values.Sort(); | ||
|
||
using var wmiConnection = new WmiConnection(@"\\.\root\StandardCimv2"); | ||
|
||
foreach (var wmiObject in wmiConnection.CreateQuery("SELECT * FROM MSFT_NetAdapter")) | ||
{ | ||
try | ||
{ | ||
// Skip non-physical adapters if instructed to do so. | ||
if (wmiObject["ConnectorPresent"] is bool isPhysical) | ||
{ | ||
if (excludeNonPhysical && !isPhysical) | ||
{ | ||
continue; | ||
} | ||
} | ||
|
||
// Skip wireless adapters if instructed to do so. | ||
if (wmiObject["NdisPhysicalMedium"] is uint ndisPhysicalMedium) | ||
{ | ||
if (excludeWireless && ndisPhysicalMedium == 9) // Native802_11 | ||
{ | ||
continue; | ||
} | ||
} | ||
if (wmiObject["PermanentAddress"] is string permanentAddress) | ||
{ | ||
// Ensure the hardware addresses are formatted as MAC addresses if possible. | ||
// This is a discrepancy between the MSFT_NetAdapter and Win32_NetworkAdapter interfaces. | ||
values.Add(MacAddressFormatter.FormatMacAddress(permanentAddress)); | ||
} | ||
} | ||
finally | ||
{ | ||
wmiObject.Dispose(); | ||
} | ||
} | ||
|
||
return values.Count > 0 | ||
? string.Join(",", values.ToArray()) | ||
: null; | ||
} | ||
|
||
/// <summary> | ||
/// Gets the component value using Win32_NetworkAdapter. | ||
/// </summary> | ||
/// <param name="excludeWireless">A value determining whether wireless devices should be excluded.</param> | ||
/// <param name="excludeNonPhysical">A value determining whether non-physical devices should be excluded.</param> | ||
/// <returns>The component value.</returns> | ||
private static string GetValueUsingWin32NetworkAdapter(bool excludeWireless, bool excludeNonPhysical) | ||
{ | ||
var values = new List<string>(); | ||
|
||
using var wmiConnection = new WmiConnection(); | ||
var wmiQuery = wmiConnection.CreateQuery("SELECT MACAddress, AdapterTypeID, PhysicalAdapter FROM Win32_NetworkAdapter"); | ||
foreach (var managementObject in wmiQuery) | ||
{ | ||
try | ||
{ | ||
// Skip non-physical adapters if instructed to do so. | ||
if (managementObject["PhysicalAdapter"] is bool isPhysical) | ||
{ | ||
if (excludeNonPhysical && !isPhysical) | ||
{ | ||
continue; | ||
} | ||
} | ||
|
||
// Skip wireless adapters if instructed to do so. | ||
if (managementObject["AdapterTypeID"] is ushort adapterTypeId) | ||
{ | ||
if (excludeWireless && adapterTypeId == 9) | ||
{ | ||
continue; | ||
} | ||
} | ||
|
||
if (managementObject["MACAddress"] is string macAddress) | ||
{ | ||
if (!string.IsNullOrEmpty(macAddress)) | ||
{ | ||
values.Add(macAddress); | ||
} | ||
} | ||
} | ||
finally | ||
{ | ||
managementObject.Dispose(); | ||
} | ||
} | ||
|
||
values.Sort(); | ||
|
||
return values.Count > 0 | ||
? string.Join(",", values.ToArray()) | ||
: null; | ||
} | ||
} |
60 changes: 60 additions & 0 deletions
60
src/DeviceId.Windows.WmiLight/Components/WmiLightSystemDriveSerialNumberDeviceIdComponent.cs
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
using System; | ||
using WmiLight; | ||
|
||
namespace DeviceId.Windows.WmiLight.Components; | ||
|
||
/// <summary> | ||
/// An implementation of <see cref="IDeviceIdComponent"/> that uses the system drive's serial number. | ||
/// </summary> | ||
public class WmiLightSystemDriveSerialNumberDeviceIdComponent() : IDeviceIdComponent | ||
{ | ||
/// <summary> | ||
/// Gets the component value. | ||
/// </summary> | ||
/// <returns>The component value.</returns> | ||
public string GetValue() | ||
{ | ||
var systemDirectory = Environment.GetFolderPath(Environment.SpecialFolder.System); | ||
var systemLogicalDiskDeviceId = systemDirectory.Substring(0, 2); | ||
|
||
// SystemDirectory can sometimes be null or empty. | ||
// See: https://github.com/dotnet/runtime/issues/21430 and https://github.com/MatthewKing/DeviceId/issues/64 | ||
if (string.IsNullOrEmpty(systemDirectory) || systemDirectory.Length < 2) | ||
{ | ||
return null; | ||
} | ||
|
||
try | ||
{ | ||
using var wmiConnection = new WmiConnection(); | ||
|
||
foreach (var logicalDisk in wmiConnection.CreateQuery($"ASSOCIATORS OF {{Win32_LogicalDisk.DeviceID=\"{systemLogicalDiskDeviceId}\"}} WHERE ResultClass = Win32_DiskPartition")) | ||
{ | ||
try | ||
{ | ||
if (logicalDisk.Class != "Win32_DiskPartition") continue; | ||
if (logicalDisk["DeviceId"] is not string diskPartitionDeviceId) continue; | ||
foreach (var diskPartitionAssociator in wmiConnection.CreateQuery( | ||
$"ASSOCIATORS OF {{Win32_DiskPartition.DeviceID=\"{diskPartitionDeviceId}\"}}")) | ||
{ | ||
if (diskPartitionAssociator.Class == "Win32_DiskDrive" | ||
&& diskPartitionAssociator["SerialNumber"] is string diskDriveSerialNumber) | ||
{ | ||
return diskDriveSerialNumber; | ||
} | ||
} | ||
} | ||
finally | ||
{ | ||
logicalDisk.Dispose(); | ||
} | ||
} | ||
} | ||
catch | ||
{ | ||
// Swallow exceptions. | ||
} | ||
|
||
return null; | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
src/DeviceId.Windows.WmiLight/DeviceId.Windows.WmiLight.csproj
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<PackageId>DeviceId.Windows.WmiLight</PackageId> | ||
<Title>DeviceId (Windows / WmiLight)</Title> | ||
<Description>Provides extra Windows-specific components (using WmiLight) for the DeviceId package.</Description> | ||
<VersionPrefix>6.6.0</VersionPrefix> | ||
</PropertyGroup> | ||
<PropertyGroup> | ||
<TargetFrameworks>netstandard2.0;.Net7.0;.Net8.0</TargetFrameworks> | ||
<LangVersion>latest</LangVersion> | ||
<GenerateDocumentationFile>true</GenerateDocumentationFile> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\DeviceId.Windows\DeviceId.Windows.csproj" /> | ||
<ProjectReference Include="..\DeviceId\DeviceId.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="WmiLight" Version="6.0.0" /> | ||
</ItemGroup> | ||
|
||
</Project> |
76 changes: 76 additions & 0 deletions
76
src/DeviceId.Windows.WmiLight/WindowsDeviceIdBuilderExtensions.cs
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
using System; | ||
using System.ComponentModel; | ||
using DeviceId.Windows.WmiLight.Components; | ||
|
||
// ReSharper disable once CheckNamespace | ||
namespace DeviceId; | ||
|
||
/// <summary> | ||
/// Extension methods for <see cref="WindowsDeviceIdBuilder"/>. | ||
/// </summary> | ||
public static class WindowsDeviceIdBuilderExtensions | ||
{ | ||
/// <summary> | ||
/// Adds the MAC address to the device identifier, optionally excluding wireless adapters and/or non-physical adapters. | ||
/// </summary> | ||
/// <param name="builder">The <see cref="WindowsDeviceIdBuilder"/> to add the component to.</param> | ||
/// <param name="excludeWireless">A value indicating whether wireless adapters should be excluded.</param> | ||
/// <param name="excludeNonPhysical">A value indicating whether non-physical adapters should be excluded.</param> | ||
/// <returns>The <see cref="WindowsDeviceIdBuilder"/> instance.</returns> | ||
public static WindowsDeviceIdBuilder AddMacAddressFromWmi(this WindowsDeviceIdBuilder builder, bool excludeWireless, bool excludeNonPhysical) | ||
{ | ||
return builder.AddComponent("MACAddress", new WmiLightMacAddressDeviceIdComponent(excludeWireless, excludeNonPhysical)); | ||
} | ||
|
||
/// <summary> | ||
/// Adds the processor ID to the device identifier. | ||
/// </summary> | ||
/// <param name="builder">The <see cref="WindowsDeviceIdBuilder"/> to add the component to.</param> | ||
/// <returns>The <see cref="WindowsDeviceIdBuilder"/> instance.</returns> | ||
public static WindowsDeviceIdBuilder AddProcessorId(this WindowsDeviceIdBuilder builder) | ||
{ | ||
return builder.AddComponent("ProcessorId", new WmiLightDeviceIdComponent("Win32_Processor", "ProcessorId")); | ||
} | ||
|
||
/// <summary> | ||
/// Adds the motherboard serial number to the device identifier. | ||
/// </summary> | ||
/// <param name="builder">The <see cref="WindowsDeviceIdBuilder"/> to add the component to.</param> | ||
/// <returns>The <see cref="WindowsDeviceIdBuilder"/> instance.</returns> | ||
public static WindowsDeviceIdBuilder AddMotherboardSerialNumber(this WindowsDeviceIdBuilder builder) | ||
{ | ||
return builder.AddComponent("MotherboardSerialNumber", new WmiLightDeviceIdComponent("Win32_BaseBoard", "SerialNumber")); | ||
} | ||
|
||
/// <summary> | ||
/// Adds the system UUID to the device identifier. | ||
/// </summary> | ||
/// <param name="builder">The <see cref="WindowsDeviceIdBuilder"/> to add the component to.</param> | ||
/// <returns>The <see cref="WindowsDeviceIdBuilder"/> instance.</returns> | ||
public static WindowsDeviceIdBuilder AddSystemUuid(this WindowsDeviceIdBuilder builder) | ||
{ | ||
return builder.AddComponent("SystemUUID", new WmiLightDeviceIdComponent("Win32_ComputerSystemProduct", "UUID")); | ||
} | ||
|
||
/// <summary> | ||
/// Adds the system serial drive number to the device identifier. | ||
/// </summary> | ||
/// <param name="builder">The <see cref="WindowsDeviceIdBuilder"/> to add the component to.</param> | ||
/// <returns>The <see cref="WindowsDeviceIdBuilder"/> instance.</returns> | ||
[EditorBrowsable(EditorBrowsableState.Never)] | ||
[Obsolete("This method name was a typo. Use AddSystemDriveSerialNumber instead.")] | ||
public static WindowsDeviceIdBuilder AddSystemSerialDriveNumber(this WindowsDeviceIdBuilder builder) | ||
{ | ||
return builder.AddComponent("SystemDriveSerialNumber", new WmiLightSystemDriveSerialNumberDeviceIdComponent()); | ||
} | ||
|
||
/// <summary> | ||
/// Adds the system serial drive number to the device identifier. | ||
/// </summary> | ||
/// <param name="builder">The <see cref="WindowsDeviceIdBuilder"/> to add the component to.</param> | ||
/// <returns>The <see cref="WindowsDeviceIdBuilder"/> instance.</returns> | ||
public static WindowsDeviceIdBuilder AddSystemDriveSerialNumber(this WindowsDeviceIdBuilder builder) | ||
{ | ||
return builder.AddComponent("SystemDriveSerialNumber", new WmiLightSystemDriveSerialNumberDeviceIdComponent()); | ||
} | ||
} |
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
Oops, something went wrong.