// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. // If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. // Copyright (C) LibreHardwareMonitor and Contributors. // Partial Copyright (C) Michael Möller and Contributors. // All Rights Reserved. using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using LibreHardwareMonitor.Hardware.Battery; using LibreHardwareMonitor.Hardware.Controller.AeroCool; using LibreHardwareMonitor.Hardware.Controller.AquaComputer; using LibreHardwareMonitor.Hardware.Controller.Arctic; using LibreHardwareMonitor.Hardware.Controller.Heatmaster; using LibreHardwareMonitor.Hardware.Controller.MSI; using LibreHardwareMonitor.Hardware.Controller.Nzxt; using LibreHardwareMonitor.Hardware.Controller.Razer; using LibreHardwareMonitor.Hardware.Controller.TBalancer; using LibreHardwareMonitor.Hardware.Cpu; using LibreHardwareMonitor.Hardware.Gpu; using LibreHardwareMonitor.Hardware.Memory; using LibreHardwareMonitor.Hardware.Motherboard; using LibreHardwareMonitor.Hardware.Network; using LibreHardwareMonitor.Hardware.PowerMonitor; using LibreHardwareMonitor.Hardware.Psu.Corsair; using LibreHardwareMonitor.Hardware.Psu.Msi; using LibreHardwareMonitor.Hardware.Storage; namespace LibreHardwareMonitor.Hardware; /// /// Stores all hardware groups and decides which devices should be enabled and updated. /// public class Computer : IComputer { private const string DeferNetworkDetectionEnvironmentVariable = "LHM_NETWORK_DEFER_DETECTION"; private const string DeferNetworkDetectionSetting = "network.deferDetection"; private const string DeferNvidiaDetectionEnvironmentVariable = "LHM_NVIDIA_DEFER_DETECTION"; private const string DeferNvidiaDetectionSetting = "nvidia.deferDetection"; private const string DeferStorageDetectionEnvironmentVariable = "LHM_STORAGE_DEFER_DETECTION"; private const string DeferStorageDetectionSetting = "storage.deferDetection"; private const string DeferIntelGpuDetectionEnvironmentVariable = "LHM_INTEL_GPU_DEFER_DETECTION"; private const string DeferIntelGpuDetectionSetting = "gpu.deferIntelDetection"; private const string DeferControllerDetectionEnvironmentVariable = "LHM_CONTROLLER_DEFER_DETECTION"; private const string DeferControllerDetectionSetting = "controller.deferDetection"; private const string DeferPsuDetectionEnvironmentVariable = "LHM_PSU_DEFER_DETECTION"; private const string DeferPsuDetectionSetting = "psu.deferDetection"; private readonly object _deferredGroupLock = new(); private readonly List _groups = new(); private readonly object _lock = new(); private readonly ISettings _settings; private bool _batteryEnabled; private bool _controllerEnabled; private bool _cpuEnabled; private bool _gpuEnabled; private bool _powerMonitorEnabled; private bool _memoryEnabled; private bool _motherboardEnabled; private bool _networkEnabled; private bool _open; private bool _psuEnabled; private SMBios _smbios; private bool _storageEnabled; private CancellationTokenSource _deferredGroupCancellationTokenSource; private TaskCompletionSource _deferredGroupCompletionSource = CreateCompletedTaskCompletionSource(); private List _deferredGroupTasks = []; /// /// Creates a new instance with basic initial . /// public Computer() { _settings = new Settings(); } /// /// Creates a new instance with additional . /// /// Computer settings that will be transferred to each . public Computer(ISettings settings) { _settings = settings ?? new Settings(); } /// public event HardwareEventHandler HardwareAdded; /// public event HardwareEventHandler HardwareRemoved; public event EventHandler HardwareDiscoveryCompleted; public Task HardwareDiscoveryTask { get { lock (_deferredGroupLock) return _deferredGroupCompletionSource.Task; } } /// public IList Hardware { get { lock (_lock) { List list = new(); foreach (IGroup group in _groups) list.AddRange(group.Hardware); return list; } } } /// public bool IsBatteryEnabled { get { return _batteryEnabled; } set { if (_open && value != _batteryEnabled) { if (value) { Add(new BatteryGroup(_settings)); } else { RemoveType(); } } _batteryEnabled = value; } } /// public bool IsControllerEnabled { get { return _controllerEnabled; } set { if (_open && value != _controllerEnabled) { if (value) { Add(new TBalancerGroup(_settings)); Add(new HeatmasterGroup(_settings)); Add(new AquaComputerGroup(_settings)); Add(new AeroCoolGroup(_settings)); Add(new NzxtGroup(_settings)); Add(new RazerGroup(_settings)); Add(new ArcticGroup(_settings)); Add(new MsiGroup(_settings)); } else { RemoveType(); RemoveType(); RemoveType(); RemoveType(); RemoveType(); RemoveType(); RemoveType(); RemoveType(); } } _controllerEnabled = value; } } /// public bool IsCpuEnabled { get { return _cpuEnabled; } set { if (_open && value != _cpuEnabled) { if (value) Add(new CpuGroup(_settings)); else RemoveType(); } _cpuEnabled = value; } } /// public bool IsGpuEnabled { get { return _gpuEnabled; } set { if (_open && value != _gpuEnabled) { if (value) { Add(new AmdGpuGroup(_settings)); Add(new NvidiaGroup(_settings)); if (_cpuEnabled) Add(new IntelGpuGroup(GetIntelCpus(), _settings)); } else { RemoveType(); RemoveType(); RemoveType(); } } _gpuEnabled = value; } } /// public bool IsPowerMonitorEnabled { get { return _powerMonitorEnabled; } set { if (_open && value != _powerMonitorEnabled) { if (value) Add(new PowerMonitorGroup(_settings)); else RemoveType(); } _powerMonitorEnabled = value; } } /// public bool IsMemoryEnabled { get { return _memoryEnabled; } set { if (_open && value != _memoryEnabled) { if (value) Add(new MemoryGroup(_settings)); else RemoveType(); } _memoryEnabled = value; } } /// public bool IsMotherboardEnabled { get { return _motherboardEnabled; } set { if (_open && value != _motherboardEnabled) { if (value) Add(new MotherboardGroup(_smbios, _settings)); else RemoveType(); } _motherboardEnabled = value; } } /// public bool IsNetworkEnabled { get { return _networkEnabled; } set { if (_open && value != _networkEnabled) { if (value) Add(new NetworkGroup(_settings)); else RemoveType(); } _networkEnabled = value; } } /// public bool IsPsuEnabled { get { return _psuEnabled; } set { if (_open && value != _psuEnabled) { if (value) { Add(new CorsairPsuGroup(_settings)); Add(new MsiPsuGroup(_settings)); } else { RemoveType(); RemoveType(); } } _psuEnabled = value; } } /// public bool IsStorageEnabled { get { return _storageEnabled; } set { if (_open && value != _storageEnabled) { if (value) Add(new StorageGroup(_settings)); else RemoveType(); } _storageEnabled = value; } } /// /// Contains computer information table read in accordance with System Management BIOS (SMBIOS) Reference Specification. /// public SMBios SMBios { get { if (!_open) throw new InvalidOperationException("SMBIOS cannot be accessed before opening."); return _smbios; } } //// public string GetReport() { lock (_lock) { using StringWriter w = new(CultureInfo.InvariantCulture); w.WriteLine(); w.WriteLine(nameof(LibreHardwareMonitor) + " Report"); w.WriteLine(); Version version = typeof(Computer).Assembly.GetName().Version; NewSection(w); w.Write("Version: "); w.WriteLine(version.ToString()); w.WriteLine(); NewSection(w); w.Write("Common Language Runtime: "); w.WriteLine(Environment.Version.ToString()); w.Write("Operating System: "); w.WriteLine(Environment.OSVersion.ToString()); w.Write("Process Type: "); w.WriteLine(IntPtr.Size == 4 ? "32-Bit" : "64-Bit"); w.WriteLine(); NewSection(w); w.WriteLine("Sensors"); w.WriteLine(); foreach (IGroup group in _groups) { foreach (IHardware hardware in group.Hardware) ReportHardwareSensorTree(hardware, w, string.Empty); } w.WriteLine(); NewSection(w); w.WriteLine("Parameters"); w.WriteLine(); foreach (IGroup group in _groups) { foreach (IHardware hardware in group.Hardware) ReportHardwareParameterTree(hardware, w, string.Empty); } w.WriteLine(); foreach (IGroup group in _groups) { string report = group.GetReport(); if (!string.IsNullOrEmpty(report)) { NewSection(w); w.Write(report); } foreach (IHardware hardware in group.Hardware) ReportHardware(hardware, w); } return w.ToString(); } } /// /// Triggers the method for the given observer. /// /// Observer who call to devices. public void Accept(IVisitor visitor) { if (visitor == null) throw new ArgumentNullException(nameof(visitor)); visitor.VisitComputer(this); } /// /// Triggers the method with the given visitor for each device in each group. /// /// Observer who call to devices. public void Traverse(IVisitor visitor) { lock (_lock) { // Use a for-loop instead of foreach to avoid a collection modified exception after sleep, even though everything is under a lock. for (int i = 0; i < _groups.Count; i++) { IGroup group = _groups[i]; for (int j = 0; j < group.Hardware.Count; j++) group.Hardware[j].Accept(visitor); } } } private void HardwareAddedEvent(IHardware hardware) { HardwareAdded?.Invoke(hardware); } private void HardwareRemovedEvent(IHardware hardware) { HardwareRemoved?.Invoke(hardware); } private void Add(IGroup group) { if (group == null) return; lock (_lock) { if (_groups.Contains(group)) return; _groups.Add(group); if (group is IHardwareChanged hardwareChanged) { hardwareChanged.HardwareAdded += HardwareAddedEvent; hardwareChanged.HardwareRemoved += HardwareRemovedEvent; } TrackGroupHardwareDiscoveryTask(group); } if (HardwareAdded != null) { foreach (IHardware hardware in group.Hardware) HardwareAdded(hardware); } } private void Remove(IGroup group) { lock (_lock) { if (!_groups.Contains(group)) return; _groups.Remove(group); if (group is IHardwareChanged hardwareChanged) { hardwareChanged.HardwareAdded -= HardwareAddedEvent; hardwareChanged.HardwareRemoved -= HardwareRemovedEvent; } } if (HardwareRemoved != null) { foreach (IHardware hardware in group.Hardware) HardwareRemoved(hardware); } group.Close(); } private void RemoveType() where T : IGroup { List list = []; lock (_lock) { foreach (IGroup group in _groups) { if (group is T t) list.Add(t); } } foreach (T group in list) Remove(group); } /// /// If hasn't been opened before, opens , and triggers the private method depending on which categories are /// enabled. /// public void Open() { if (_open) return; OpenInternal(CancellationToken.None); } /// /// Asynchronously performs the same work as . The required global setup (SMBIOS, mutexes and /// ) and hardware group discovery run on a background thread; discovery order and behavior /// match . /// /// A task that completes once all enabled hardware groups have been discovered. public Task OpenAsync() { return OpenAsync(CancellationToken.None); } /// /// Asynchronously performs the same work as , observing the supplied /// between initialization phases. /// /// Token used to cancel initialization before it completes. /// A task that completes once all enabled hardware groups have been discovered. public Task OpenAsync(CancellationToken cancellationToken) { if (_open) return Task.CompletedTask; return Task.Run(() => OpenInternal(cancellationToken), cancellationToken); } private void OpenInternal(CancellationToken cancellationToken) { if (_open) return; StartDeferredGroupRun(); try { using HardwareStartupTrace startupTrace = HardwareStartupTrace.Create(_settings); cancellationToken.ThrowIfCancellationRequested(); _smbios = Measure(startupTrace, "SMBios", () => new SMBios()); if (Software.OperatingSystem.IsWindows8OrGreater) Measure(startupTrace, "Mutexes.Open", Mutexes.Open); else startupTrace?.Skip("Mutexes.Open", "Operating system is older than Windows 8."); cancellationToken.ThrowIfCancellationRequested(); Measure(startupTrace, "OpCode.Open", OpCode.Open); AddGroups(startupTrace, cancellationToken); CompleteDeferredGroupRunWhenRegistered(); _open = true; } catch { CancelDeferredGroupRun(); throw; } } private void AddGroups(HardwareStartupTrace startupTrace, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (_motherboardEnabled) AddMeasuredGroup(startupTrace, "MotherboardGroup", () => new MotherboardGroup(_smbios, _settings)); cancellationToken.ThrowIfCancellationRequested(); if (_cpuEnabled) AddMeasuredGroup(startupTrace, "CpuGroup", () => new CpuGroup(_settings, startupTrace)); cancellationToken.ThrowIfCancellationRequested(); if (_memoryEnabled) AddMeasuredGroup(startupTrace, "MemoryGroup", () => new MemoryGroup(_settings, startupTrace)); cancellationToken.ThrowIfCancellationRequested(); if (_gpuEnabled) { AddMeasuredGroup(startupTrace, "AmdGpuGroup", () => new AmdGpuGroup(_settings)); AddMeasuredGroup(startupTrace, "NvidiaGroup", () => new NvidiaGroup(_settings), () => _gpuEnabled, DeferNvidiaDetectionSetting, DeferNvidiaDetectionEnvironmentVariable); // Intel GPU detection is the most expensive GPU probe but only depends on the (already-created) CPU group, // so it can be deferred to the background like the other GPU/storage/network groups. if (_cpuEnabled) AddMeasuredGroup(startupTrace, "IntelGpuGroup", () => new IntelGpuGroup(GetIntelCpus(), _settings), () => _gpuEnabled && _cpuEnabled, DeferIntelGpuDetectionSetting, DeferIntelGpuDetectionEnvironmentVariable); } cancellationToken.ThrowIfCancellationRequested(); if (_powerMonitorEnabled) AddMeasuredGroup(startupTrace, "PowerMonitorGroup", () => new PowerMonitorGroup(_settings)); cancellationToken.ThrowIfCancellationRequested(); if (_controllerEnabled) { // Controllers probe USB/serial buses (with worst-case timeouts), so the whole block can be deferred to a // single background task. It stays sequential there to avoid concurrent serial-port/USB scans. if (ShouldDeferDetection(DeferControllerDetectionSetting, DeferControllerDetectionEnvironmentVariable)) { startupTrace?.Skip("ControllerGroups", "Deferred to background."); AddDeferredGroups(() => _controllerEnabled, () => new TBalancerGroup(_settings), () => new HeatmasterGroup(_settings), () => new AquaComputerGroup(_settings), () => new AeroCoolGroup(_settings), () => new NzxtGroup(_settings), () => new RazerGroup(_settings), () => new ArcticGroup(_settings), () => new MsiGroup(_settings)); } else { AddMeasuredGroup(startupTrace, "TBalancerGroup", () => new TBalancerGroup(_settings)); AddMeasuredGroup(startupTrace, "HeatmasterGroup", () => new HeatmasterGroup(_settings)); AddMeasuredGroup(startupTrace, "AquaComputerGroup", () => new AquaComputerGroup(_settings)); AddMeasuredGroup(startupTrace, "AeroCoolGroup", () => new AeroCoolGroup(_settings)); AddMeasuredGroup(startupTrace, "NzxtGroup", () => new NzxtGroup(_settings)); AddMeasuredGroup(startupTrace, "RazerGroup", () => new RazerGroup(_settings)); AddMeasuredGroup(startupTrace, "ArcticGroup", () => new ArcticGroup(_settings)); AddMeasuredGroup(startupTrace, "MsiGroup", () => new MsiGroup(_settings)); } } cancellationToken.ThrowIfCancellationRequested(); if (_storageEnabled) AddMeasuredGroup(startupTrace, "StorageGroup", () => new StorageGroup(_settings), () => _storageEnabled, DeferStorageDetectionSetting, DeferStorageDetectionEnvironmentVariable); cancellationToken.ThrowIfCancellationRequested(); if (_networkEnabled) AddMeasuredGroup(startupTrace, "NetworkGroup", () => new NetworkGroup(_settings), () => _networkEnabled, DeferNetworkDetectionSetting, DeferNetworkDetectionEnvironmentVariable); cancellationToken.ThrowIfCancellationRequested(); if (_psuEnabled) { // PSU detection probes HID devices, so it contends with the deferred controllers' USB/HID scans. Defer it // to a background task too, keeping all HID/USB probing off the critical path. if (ShouldDeferDetection(DeferPsuDetectionSetting, DeferPsuDetectionEnvironmentVariable)) { startupTrace?.Skip("PsuGroups", "Deferred to background."); AddDeferredGroups(() => _psuEnabled, () => new CorsairPsuGroup(_settings), () => new MsiPsuGroup(_settings)); } else { AddMeasuredGroup(startupTrace, "CorsairPsuGroup", () => new CorsairPsuGroup(_settings)); AddMeasuredGroup(startupTrace, "MsiPsuGroup", () => new MsiPsuGroup(_settings)); } } cancellationToken.ThrowIfCancellationRequested(); if (_batteryEnabled) AddMeasuredGroup(startupTrace, "BatteryGroup", () => new BatteryGroup(_settings)); } private static void Measure(HardwareStartupTrace startupTrace, string phase, Action action) { if (startupTrace != null) startupTrace.Measure(phase, action); else action(); } private static T Measure(HardwareStartupTrace startupTrace, string phase, Func action) { return startupTrace != null ? startupTrace.Measure(phase, action) : action(); } private void AddMeasuredGroup(HardwareStartupTrace startupTrace, string phase, Func createGroup) { Add(Measure(startupTrace, phase, createGroup)); } private void AddMeasuredGroup(HardwareStartupTrace startupTrace, string phase, Func createGroup, Func isEnabled, string deferSettingName, string deferEnvironmentVariable) { if (!ShouldDeferDetection(deferSettingName, deferEnvironmentVariable)) { AddMeasuredGroup(startupTrace, phase, createGroup); return; } startupTrace?.Skip(phase, "Deferred to background."); AddDeferredGroup(createGroup, isEnabled); } private void AddDeferredGroup(Func createGroup, Func isEnabled) { CancellationTokenSource cancellationTokenSource = _deferredGroupCancellationTokenSource; if (cancellationTokenSource == null) { if (isEnabled()) Add(createGroup()); return; } CancellationToken cancellationToken = cancellationTokenSource.Token; Task task = Task.Run(() => { IGroup group = null; try { group = createGroup(); } catch (Exception ex) { if (!cancellationToken.IsCancellationRequested) System.Diagnostics.Debug.WriteLine($"Deferred hardware detection failed: {ex}"); return; } if (group == null) return; if (cancellationToken.IsCancellationRequested || !isEnabled()) { group.Close(); return; } Add(group); }); TrackDeferredGroupTask(task); } private void AddDeferredGroups(Func isEnabled, params Func[] createGroups) { CancellationTokenSource cancellationTokenSource = _deferredGroupCancellationTokenSource; if (cancellationTokenSource == null) { foreach (Func createGroup in createGroups) { if (!isEnabled()) return; IGroup group = createGroup(); if (group != null) Add(group); } return; } CancellationToken cancellationToken = cancellationTokenSource.Token; Task task = Task.Run(() => { foreach (Func createGroup in createGroups) { if (cancellationToken.IsCancellationRequested || !isEnabled()) return; IGroup group = null; try { group = createGroup(); } catch (Exception ex) { if (!cancellationToken.IsCancellationRequested) System.Diagnostics.Debug.WriteLine($"Deferred hardware detection failed: {ex}"); continue; } if (group == null) continue; if (cancellationToken.IsCancellationRequested || !isEnabled()) { group.Close(); return; } Add(group); } }); TrackDeferredGroupTask(task); } private bool ShouldDeferDetection(string settingName, string environmentVariable) { string environmentValue = Environment.GetEnvironmentVariable(environmentVariable); if (!string.IsNullOrWhiteSpace(environmentValue)) return IsTruthy(environmentValue); return IsTruthy(_settings.GetValue(settingName, "false")); } private static bool IsTruthy(string value) { return value.Equals("1", StringComparison.OrdinalIgnoreCase) || value.Equals("true", StringComparison.OrdinalIgnoreCase) || value.Equals("yes", StringComparison.OrdinalIgnoreCase) || value.Equals("on", StringComparison.OrdinalIgnoreCase); } private void StartDeferredGroupRun() { CancelDeferredGroupRun(); lock (_deferredGroupLock) { _deferredGroupCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); _deferredGroupTasks = []; } _deferredGroupCancellationTokenSource = new CancellationTokenSource(); } private void CancelDeferredGroupRun() { CancellationTokenSource cancellationTokenSource = _deferredGroupCancellationTokenSource; _deferredGroupCancellationTokenSource = null; TaskCompletionSource completionSource; lock (_deferredGroupLock) { completionSource = _deferredGroupCompletionSource; _deferredGroupTasks = []; } if (cancellationTokenSource == null) { completionSource.TrySetCanceled(); return; } cancellationTokenSource.Cancel(); cancellationTokenSource.Dispose(); completionSource.TrySetCanceled(); } private static TaskCompletionSource CreateCompletedTaskCompletionSource() { TaskCompletionSource completionSource = new(TaskCreationOptions.RunContinuationsAsynchronously); completionSource.SetResult(); return completionSource; } private void CompleteDeferredGroupRun(TaskCompletionSource completionSource) { bool completed; lock (_deferredGroupLock) { if (!ReferenceEquals(completionSource, _deferredGroupCompletionSource)) return; completed = completionSource.TrySetResult(); } if (completed) HardwareDiscoveryCompleted?.Invoke(this, EventArgs.Empty); } private void CompleteDeferredGroupRunWhenRegistered() { TaskCompletionSource completionSource; Task[] tasks; lock (_deferredGroupLock) { completionSource = _deferredGroupCompletionSource; tasks = _deferredGroupTasks.ToArray(); } if (tasks.Length == 0) { CompleteDeferredGroupRun(completionSource); return; } _ = Task.WhenAll(tasks).ContinueWith(_ => CompleteDeferredGroupRun(completionSource), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); } private void TrackDeferredGroupTask(Task task) { lock (_deferredGroupLock) _deferredGroupTasks.Add(task); } private void TrackGroupHardwareDiscoveryTask(IGroup group) { if (group is not IHardwareDiscoveryTask hardwareDiscoveryTask || hardwareDiscoveryTask.HardwareDiscoveryTask.IsCompleted) return; TrackDeferredGroupTask(hardwareDiscoveryTask.HardwareDiscoveryTask); } private static void NewSection(TextWriter writer) { for (int i = 0; i < 8; i++) writer.Write("----------"); writer.WriteLine(); writer.WriteLine(); } private static int CompareSensor(ISensor a, ISensor b) { int c = a.SensorType.CompareTo(b.SensorType); if (c == 0) return a.Index.CompareTo(b.Index); return c; } private static void ReportHardwareSensorTree(IHardware hardware, TextWriter w, string space) { w.WriteLine("{0}|", space); w.WriteLine("{0}+- {1} ({2})", space, hardware.Name, hardware.Identifier); ISensor[] sensors = hardware.Sensors; Array.Sort(sensors, CompareSensor); foreach (ISensor sensor in sensors) w.WriteLine("{0}| +- {1,-14} : {2,8:G6} {3,8:G6} {4,8:G6} ({5})", space, sensor.Name, sensor.Value, sensor.Min, sensor.Max, sensor.Identifier); foreach (IHardware subHardware in hardware.SubHardware) ReportHardwareSensorTree(subHardware, w, "| "); } private static void ReportHardwareParameterTree(IHardware hardware, TextWriter w, string space) { w.WriteLine("{0}|", space); w.WriteLine("{0}+- {1} ({2})", space, hardware.Name, hardware.Identifier); ISensor[] sensors = hardware.Sensors; Array.Sort(sensors, CompareSensor); foreach (ISensor sensor in sensors) { string innerSpace = space + "| "; if (sensor.Parameters.Count > 0) { w.WriteLine("{0}|", innerSpace); w.WriteLine("{0}+- {1} ({2})", innerSpace, sensor.Name, sensor.Identifier); foreach (IParameter parameter in sensor.Parameters) { string innerInnerSpace = innerSpace + "| "; w.WriteLine("{0}+- {1} : {2}", innerInnerSpace, parameter.Name, string.Format(CultureInfo.InvariantCulture, "{0} : {1}", parameter.DefaultValue, parameter.Value)); } } } foreach (IHardware subHardware in hardware.SubHardware) ReportHardwareParameterTree(subHardware, w, "| "); } private static void ReportHardware(IHardware hardware, TextWriter w) { string hardwareReport = hardware.GetReport(); if (!string.IsNullOrEmpty(hardwareReport)) { NewSection(w); w.Write(hardwareReport); } foreach (IHardware subHardware in hardware.SubHardware) ReportHardware(subHardware, w); } /// /// If opened before, removes all and triggers . /// public void Close() { if (!_open) return; CancelDeferredGroupRun(); lock (_lock) { while (_groups.Count > 0) { IGroup group = _groups[_groups.Count - 1]; Remove(group); } } OpCode.Close(); Mutexes.Close(); _smbios = null; _open = false; } /// /// If opened before, removes all and recreates it. /// public void Reset() { if (!_open) return; StartDeferredGroupRun(); RemoveGroups(); AddGroups(null, CancellationToken.None); CompleteDeferredGroupRunWhenRegistered(); } private void RemoveGroups() { lock (_lock) { while (_groups.Count > 0) { IGroup group = _groups[_groups.Count - 1]; Remove(group); } } } private List GetIntelCpus() { // Create a temporary cpu group if one has not been added. lock (_lock) { IGroup cpuGroup = _groups.Find(x => x is CpuGroup) ?? new CpuGroup(_settings); return cpuGroup.Hardware.Select(x => x as IntelCpu).ToList(); } } /// /// specific additional settings passed to its . /// private class Settings : ISettings { public bool Contains(string name) { return false; } public void SetValue(string name, string value) { } public string GetValue(string name, string value) { return value; } public void Remove(string name) { } } }