diff --git a/src/DeviceId/Components/FileDeviceIdComponent.cs b/src/DeviceId/Components/FileDeviceIdComponent.cs index 8d1e3da..aab700c 100644 --- a/src/DeviceId/Components/FileDeviceIdComponent.cs +++ b/src/DeviceId/Components/FileDeviceIdComponent.cs @@ -85,6 +85,11 @@ public string GetValue() catch (UnauthorizedAccessException) { // Can fail if we have no permissions to access the file. + throw new DeviceIdComponentFailedToObtainValueException("Can't access file"); + } + catch(Exception e) + { + throw new DeviceIdComponentFailedToObtainValueException("Failed reading file", e); } } diff --git a/src/DeviceId/Components/FileTokenDeviceIdComponent.cs b/src/DeviceId/Components/FileTokenDeviceIdComponent.cs index d6fe671..e16fdc6 100644 --- a/src/DeviceId/Components/FileTokenDeviceIdComponent.cs +++ b/src/DeviceId/Components/FileTokenDeviceIdComponent.cs @@ -49,7 +49,10 @@ public string GetValue() var value = Encoding.ASCII.GetString(bytes); return value; } - catch { } + catch(Exception e) + { + throw new DeviceIdComponentFailedToObtainValueException("Failed to read file contents", e); + } } else { diff --git a/src/DeviceId/Components/NetworkAdapterDeviceIdComponent.cs b/src/DeviceId/Components/NetworkAdapterDeviceIdComponent.cs index 4616fdb..75ab26c 100644 --- a/src/DeviceId/Components/NetworkAdapterDeviceIdComponent.cs +++ b/src/DeviceId/Components/NetworkAdapterDeviceIdComponent.cs @@ -1,6 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; -using System.Management; using System.Net.NetworkInformation; namespace DeviceId.Components @@ -61,15 +61,18 @@ public string GetValue() // First attempt to retrieve the addresses using the CIMv2 interface. values = GetMacAddressesUsingCimV2(); } - catch (ManagementException ex) + catch { - // In case we are notified of an invalid namespace, attempt to lookup the adapters using WMI. + // In case we are notified of an exception (usually, invalid namespace), attempt to lookup the adapters using WMI. // Could avoid this catch by manually checking for the CIMv2 namespace. - - if (ex.ErrorCode == ManagementStatus.InvalidNamespace) + try { values = GetMacAddressesUsingWmi(); } + catch + { + // Can also fail (for example, some Windows 7 machines) + } } } @@ -81,20 +84,18 @@ public string GetValue() values = NetworkInterface.GetAllNetworkInterfaces() .Where(x => !_excludeWireless || x.NetworkInterfaceType != NetworkInterfaceType.Wireless80211) .Select(x => x.GetPhysicalAddress().ToString()) - .Where(x => x != "000000000000") + .Where(x => x != "" && x != "000000000000") .Select(x => FormatMacAddress(x)) .ToList(); } catch { - + throw new DeviceIdComponentFailedToObtainValueException("Failed collecting network adapters"); } } if (values != null) - { values.Sort(); - } return (values != null && values.Count > 0) ? string.Join(",", values.ToArray()) @@ -109,36 +110,29 @@ internal List GetMacAddressesUsingWmi() { var values = new List(); - try + var adapters = WmiHelper.GetWMIInstances(@"root\cimv2", "Win32_NetworkAdapter", new string[] { "MACAddress", "PhysicalAdapter" }); + + foreach (var adapter in adapters) { - using var managementObjectSearcher = new ManagementObjectSearcher("select MACAddress, PhysicalAdapter from Win32_NetworkAdapter"); - using var managementObjectCollection = managementObjectSearcher.Get(); - foreach (var managementObject in managementObjectCollection) - { - try + try + { + var isPhysical = Boolean.Parse(adapter["PhysicalAdapter"] as string); + + if (_excludeNonPhysical && !isPhysical) { - // Skip non physcial adapters if instructed to do so. - var isPhysical = (bool)managementObject["PhysicalAdapter"]; - if (_excludeNonPhysical && !isPhysical) - { - continue; - } - - var macAddress = (string)managementObject["MACAddress"]; - if (!string.IsNullOrEmpty(macAddress)) - { - values.Add(macAddress); - } + continue; } - finally + + var macAddress = adapter["MACAddress"] as string; + if (!string.IsNullOrEmpty(macAddress)) { - managementObject.Dispose(); + values.Add(macAddress); } } - } - catch - { - + catch + { + + } } return values; @@ -152,28 +146,29 @@ internal List GetMacAddressesUsingCimV2() { var values = new List(); - using var managementClass = new ManagementClass("root/StandardCimv2", "MSFT_NetAdapter", new ObjectGetOptions { }); + var adapters = WmiHelper.GetWMIInstances(@"root\StandardCimv2", "MSFT_NetAdapter", new string[] { "ConnectorPresent", "NdisPhysicalMedium", "PermanentAddress" }); - foreach (var managementInstance in managementClass.GetInstances()) + foreach (var adapter in adapters) { try { + var isPhysical = Boolean.Parse(adapter["ConnectorPresent"] as string); + var ndisMedium = UInt32.Parse(adapter["NdisPhysicalMedium"] as string); + // Skip non physcial adapters if instructed to do so. - var isPhysical = (bool)managementInstance["ConnectorPresent"]; if (_excludeNonPhysical && !isPhysical) { continue; } // Skip wireless adapters if instructed to do so. - var ndisMedium = (uint)managementInstance["NdisPhysicalMedium"]; if (_excludeWireless && ndisMedium == 9) // Native802_11 { continue; } // Add the MAC address to the list of values. - var value = managementInstance["PermanentAddress"] as string; + var value = adapter["PermanentAddress"] as string; if (value != null) { // Ensure the hardware addresses are formatted as MAC addresses if possible. @@ -182,9 +177,9 @@ internal List GetMacAddressesUsingCimV2() values.Add(value); } } - finally + catch { - managementInstance.Dispose(); + } } diff --git a/src/DeviceId/Components/RegistryValueDeviceIdComponent.cs b/src/DeviceId/Components/RegistryValueDeviceIdComponent.cs index bd6ab27..570da70 100644 --- a/src/DeviceId/Components/RegistryValueDeviceIdComponent.cs +++ b/src/DeviceId/Components/RegistryValueDeviceIdComponent.cs @@ -1,4 +1,5 @@ -using Microsoft.Win32; +using System; +using Microsoft.Win32; namespace DeviceId.Components { @@ -46,9 +47,9 @@ public string GetValue() var value = Registry.GetValue(_key, _valueName, null); return value?.ToString(); } - catch + catch(Exception e) { - return null; + throw new DeviceIdComponentFailedToObtainValueException(String.Format("Failed to read registry value ({0}\\{1})", _key, _valueName), e); } } } diff --git a/src/DeviceId/Components/SystemDriveSerialNumberDeviceIdComponent.cs b/src/DeviceId/Components/SystemDriveSerialNumberDeviceIdComponent.cs index f48b09c..7e5cd31 100644 --- a/src/DeviceId/Components/SystemDriveSerialNumberDeviceIdComponent.cs +++ b/src/DeviceId/Components/SystemDriveSerialNumberDeviceIdComponent.cs @@ -1,5 +1,5 @@ using System; -using System.Management; +using System.Collections.Generic; namespace DeviceId.Components { @@ -24,24 +24,19 @@ public SystemDriveSerialNumberDeviceIdComponent() { } /// The component value. public string GetValue() { - var systemLogicalDiskDeviceId = Environment.GetFolderPath(Environment.SpecialFolder.System).Substring(0, 2); - - var queryString = $"SELECT * FROM Win32_LogicalDisk where DeviceId = '{systemLogicalDiskDeviceId}'"; - using var searcher = new ManagementObjectSearcher(queryString); + try + { + var systemLogicalDiskDeviceId = Environment.GetFolderPath(Environment.SpecialFolder.System).Substring(0, 2); - foreach (ManagementObject disk in searcher.Get()) + var logicalDiskToPartition = Array.Find(WmiHelper.GetWMIInstances(@"root\cimv2", "Win32_LogicalDiskToPartition"), logicalDriveToPartition => (logicalDriveToPartition["Dependent"] as IDictionary)["DeviceID"] as string == systemLogicalDiskDeviceId); + var partition = Array.Find(WmiHelper.GetWMIInstances(@"root\cimv2", "Win32_DiskDriveToDiskPartition"), partition => (partition["Dependent"] as IDictionary)["DeviceID"] as string == (logicalDiskToPartition["Antecedent"] as IDictionary)["DeviceID"] as string); + var diskdrive = Array.Find(WmiHelper.GetWMIInstances(@"root\cimv2", "Win32_DiskDrive "), diskDriveToPartition => diskDriveToPartition["DeviceID"] as string == (partition["Antecedent"] as IDictionary)["DeviceID"] as string); + return diskdrive["SerialNumber"] as string; + } + catch(Exception e) { - foreach (ManagementObject partition in disk.GetRelated("Win32_DiskPartition")) - { - foreach (ManagementObject drive in partition.GetRelated("Win32_DiskDrive")) - { - var serialNumber = drive["SerialNumber"] as string; - return serialNumber; - } - } + throw new DeviceIdComponentFailedToObtainValueException("Failed to GetValue() in SystemDriveSerialNumberDeviceIdComponent", e); } - - return null; } } } diff --git a/src/DeviceId/Components/WmiDeviceIdComponent.cs b/src/DeviceId/Components/WmiDeviceIdComponent.cs index f07b73a..13bec9f 100644 --- a/src/DeviceId/Components/WmiDeviceIdComponent.cs +++ b/src/DeviceId/Components/WmiDeviceIdComponent.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Management; namespace DeviceId.Components { @@ -44,29 +43,16 @@ public string GetValue() { var values = new List(); - try + foreach (var obj in WmiHelper.GetWMIInstances(@"root\cimv2", _wmiClass)) { - using var managementObjectSearcher = new ManagementObjectSearcher($"SELECT {_wmiProperty} FROM {_wmiClass}"); - using var managementObjectCollection = managementObjectSearcher.Get(); - foreach (var managementObject in managementObjectCollection) + try { - try - { - var value = managementObject[_wmiProperty] as string; - if (value != null) - { - values.Add(value); - } - } - finally - { - managementObject.Dispose(); - } + values.Add(((IDictionary)obj)[_wmiProperty].ToString()); } - } - catch - { + catch + { + } } values.Sort(); diff --git a/src/DeviceId/DeviceId.csproj b/src/DeviceId/DeviceId.csproj index fc36b6a..1d19974 100644 --- a/src/DeviceId/DeviceId.csproj +++ b/src/DeviceId/DeviceId.csproj @@ -34,7 +34,7 @@ - + @@ -44,5 +44,4 @@ false - diff --git a/src/DeviceId/DeviceIdComponentFailedToObtainValueException.cs b/src/DeviceId/DeviceIdComponentFailedToObtainValueException.cs new file mode 100644 index 0000000..5e15f9f --- /dev/null +++ b/src/DeviceId/DeviceIdComponentFailedToObtainValueException.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace DeviceId +{ + class DeviceIdComponentFailedToObtainValueException : Exception + { + // + // Summary: + // Initializes a new instance of the DeviceIDComponentException class. + public DeviceIdComponentFailedToObtainValueException() + { + + } + + // + // Summary: + // Initializes a new instance of the DeviceIDComponentException class with a specified error + // message. + // + // Parameters: + // message: + // The message that describes the error. + public DeviceIdComponentFailedToObtainValueException(string message) : base(message) + { + } + + // + // Summary: + // Initializes a new instance of the DeviceIDComponentException class with a specified error + // message and a reference to the inner exception that is the cause of this exception. + // + // Parameters: + // message: + // The error message that explains the reason for the exception. + // + // innerException: + // The exception that is the cause of the current exception, or a null reference + // (Nothing in Visual Basic) if no inner exception is specified. + public DeviceIdComponentFailedToObtainValueException(string message, Exception innerException): base(message, innerException) + { + } + } +} diff --git a/src/DeviceId/Formatters/HashDeviceIdFormatter.cs b/src/DeviceId/Formatters/HashDeviceIdFormatter.cs index 9e60cdd..b201718 100644 --- a/src/DeviceId/Formatters/HashDeviceIdFormatter.cs +++ b/src/DeviceId/Formatters/HashDeviceIdFormatter.cs @@ -44,11 +44,23 @@ public string GetDeviceId(IEnumerable components) throw new ArgumentNullException(nameof(components)); } - var value = string.Join(",", components.OrderBy(x => x.Name).Select(x => x.GetValue()).ToArray()); + var value = string.Join(",", components.OrderBy(x => x.Name).Select(x => GetValue(x)).ToArray()); var bytes = Encoding.UTF8.GetBytes(value); using var algorithm = _hashAlgorithm.Invoke(); var hash = algorithm.ComputeHash(bytes); return _byteArrayEncoder.Encode(hash); } + + private string GetValue(IDeviceIdComponent component) + { + try + { + return component.GetValue(); + } + catch(DeviceIdComponentFailedToObtainValueException) + { + return ""; + } + } } } diff --git a/src/DeviceId/Formatters/StringDeviceIdFormatter.cs b/src/DeviceId/Formatters/StringDeviceIdFormatter.cs index 4de42c8..68952d2 100644 --- a/src/DeviceId/Formatters/StringDeviceIdFormatter.cs +++ b/src/DeviceId/Formatters/StringDeviceIdFormatter.cs @@ -49,7 +49,19 @@ public string GetDeviceId(IEnumerable components) throw new ArgumentNullException(nameof(components)); } - return string.Join(_delimiter, components.OrderBy(x => x.Name).Select(x => _encoder.Encode(x)).ToArray()); + return string.Join(_delimiter, components.OrderBy(x => x.Name).Select(x => GetValue(x)).ToArray()); + } + + private string GetValue(IDeviceIdComponent component) + { + try + { + return _encoder.Encode(component); + } + catch (DeviceIdComponentFailedToObtainValueException) + { + return ""; + } } } } diff --git a/src/DeviceId/Formatters/XmlDeviceIdFormatter.cs b/src/DeviceId/Formatters/XmlDeviceIdFormatter.cs index d9088bc..e674321 100644 --- a/src/DeviceId/Formatters/XmlDeviceIdFormatter.cs +++ b/src/DeviceId/Formatters/XmlDeviceIdFormatter.cs @@ -61,9 +61,17 @@ private XElement GetElement(IEnumerable components) /// An representing the specified instance. private XElement GetElement(IDeviceIdComponent component) { - return new XElement("Component", - new XAttribute("Name", component.Name), - new XAttribute("Value", _encoder.Encode(component))); + try + { + return new XElement("Component", + new XAttribute("Name", component.Name), + new XAttribute("Value", _encoder.Encode(component))); + } + catch + { + return new XElement("Component", + new XAttribute("Name", component.Name)); + } } } } diff --git a/src/DeviceId/WmiHelper.cs b/src/DeviceId/WmiHelper.cs new file mode 100644 index 0000000..89c1e39 --- /dev/null +++ b/src/DeviceId/WmiHelper.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +#if NETSTANDARD +using System.Dynamic; +using Microsoft.Management.Infrastructure; +#endif +#if NETFRAMEWORK +using System.Management; +#endif +namespace DeviceId +{ + class WmiHelper + { + /// + /// Get an IDictionary representation of WMI objects of a specified class + /// + /// The scope in which to look for objects. + /// The WMI class name. + public static IDictionary[] GetWMIInstances(string wmiNamespace, string objectClass) + { + return GetWMIInstances(wmiNamespace, objectClass, new string[0]); + } + +#if NETSTANDARD + /// + /// Get an IDictionary representation of WMI objects of a specified class + /// + /// The scope in which to look for objects. + /// The WMI class name. + /// The list of attributes to obtain (or null for all attributes). + public static IDictionary[] GetWMIInstances(string wmiNamespace, string objectClass, string[] attributes) + { + try + { + string attributesSelector = (attributes != null && attributes.Length > 0) ? String.Join(",", attributes) : "*"; + + return CimSession.Create(null) // null instead of localhost which would otherwise require certain MMI services running + .QueryInstances(wmiNamespace, "WQL", $"SELECT {attributesSelector} FROM {objectClass}").Select((obj) => CimInstanceToExpandoObject(obj)).ToArray(); + } + catch(CimException e) + { + throw new DeviceIdComponentFailedToObtainValueException(String.Format("Failed in GetWMIInstances({0},{1})", wmiNamespace, objectClass), e); + } + } + + /// + /// Get an ExpandoObject representation of a CimInstance object + /// + /// The object to convert. + private static ExpandoObject CimInstanceToExpandoObject(CimInstance obj) + { + try + { + ExpandoObject result = new ExpandoObject(); + var resultDictionary = (IDictionary)result; + + foreach (var property in obj.CimInstanceProperties.Where(property => property.Value != null)) + resultDictionary[property.Name] = property.Value is CimInstance ? CimInstanceToExpandoObject(property.Value as CimInstance) : property.Value.ToString(); + + return result; + } + finally + { + obj.Dispose(); + } + } +#endif +#if NETFRAMEWORK + /// + /// Get an IDictionary representation of WMI objects of a specified class + /// + /// The scope in which to look for objects. + /// The WMI class name. + /// The list of attributes to obtain (or null for all attributes). + public static IDictionary[] GetWMIInstances(string wmiNamespace, string objectClass, string[] attributes) + { + try + { + string attributesSelector = attributes.Length > 0 ? String.Join(",", attributes) : "*"; + using var managementObjectSearcher = new ManagementObjectSearcher(wmiNamespace, $"select {attributesSelector} from {objectClass}"); + using var managementObjectCollection = managementObjectSearcher.Get(); + + return managementObjectCollection.Cast().Select(obj => ManagementObjectToDictionary(obj)).ToArray(); + } + catch(ManagementException e) + { + throw new DeviceIdComponentFailedToObtainValueException(String.Format("Failed in GetWMIInstances({0},{1})", wmiNamespace, objectClass), e); + } + } + + /// + /// Get a Dictionary representation of a ManagementBaseObject object + /// + /// The object to convert. + private static Dictionary ManagementObjectToDictionary(ManagementBaseObject obj) + { + try + { + return obj.Properties.Cast().ToDictionary(property => property.Name, property => + property.Value is ManagementBaseObject ? ManagementObjectToDictionary(property.Value as ManagementBaseObject) as object : property.Value.ToString()); + } + finally + { + obj.Dispose(); + } + } +#endif + } +}