using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using ResourceMonitorService.Configuration; using ResourceMonitorService.Models; using System.Diagnostics; using System.Management; namespace ResourceMonitorService.Services { public interface IResourceMonitorService { Task GetResourceUsageAsync(); Task GetCpuUsageAsync(); Task GetMemoryUsageAsync(); Task GetGpuUsageAsync(); Task> GetDiskUsageAsync(); Task GetNetworkUsageAsync(); Task> GetTopProcessesAsync(int count = 10); } public class ResourceMonitorService : IResourceMonitorService { private readonly ILogger _logger; private readonly MonitoringSettings _settings; private readonly Dictionary _counters; private readonly Dictionary _previousNetworkBytes; private readonly Dictionary _previousNetworkTime; public ResourceMonitorService(ILogger logger, IOptions settings) { _logger = logger; _settings = settings.Value; _counters = new Dictionary(); _previousNetworkBytes = new Dictionary(); _previousNetworkTime = new Dictionary(); InitializeCounters(); } private void InitializeCounters() { try { #pragma warning disable CA1416 // Validate platform compatibility _counters["cpu"] = new PerformanceCounter("Processor", "% Processor Time", "_Total"); _counters["memory_available"] = new PerformanceCounter("Memory", "Available MBytes"); if (_settings.EnableNetworkMonitoring) { _counters["network_bytes_sent"] = new PerformanceCounter("Network Interface", "Bytes Sent/sec", "*"); _counters["network_bytes_received"] = new PerformanceCounter("Network Interface", "Bytes Received/sec", "*"); } if (_settings.EnableDiskMonitoring) { _counters["disk_read"] = new PerformanceCounter("PhysicalDisk", "Disk Read Bytes/sec", "_Total"); _counters["disk_write"] = new PerformanceCounter("PhysicalDisk", "Disk Write Bytes/sec", "_Total"); _counters["disk_time"] = new PerformanceCounter("PhysicalDisk", "% Disk Time", "_Total"); } #pragma warning restore CA1416 // Validate platform compatibility // Initialize counters with first reading foreach (var counter in _counters.Values) { try { counter.NextValue(); } catch (Exception ex) { _logger.LogWarning(ex, "Failed to initialize performance counter: {CounterName}", counter.CounterName); } } } catch (Exception ex) { _logger.LogError(ex, "Failed to initialize performance counters"); } } public async Task GetResourceUsageAsync() { var timestamp = DateTime.Now; var tasks = new List { Task.Run(async () => await GetCpuUsageAsync()), Task.Run(async () => await GetMemoryUsageAsync()) }; if (_settings.EnableGpuMonitoring) tasks.Add(Task.Run(async () => await GetGpuUsageAsync())); if (_settings.EnableDiskMonitoring) tasks.Add(Task.Run(async () => await GetDiskUsageAsync())); if (_settings.EnableNetworkMonitoring) tasks.Add(Task.Run(async () => await GetNetworkUsageAsync())); if (_settings.EnableProcessMonitoring) tasks.Add(Task.Run(async () => await GetTopProcessesAsync(_settings.MaxProcessesToTrack))); await Task.WhenAll(tasks); return new ResourceUsage { Timestamp = timestamp, CPU = await GetCpuUsageAsync(), Memory = await GetMemoryUsageAsync(), GPU = _settings.EnableGpuMonitoring ? await GetGpuUsageAsync() : new GpuUsage(), Disks = _settings.EnableDiskMonitoring ? await GetDiskUsageAsync() : new List(), Network = _settings.EnableNetworkMonitoring ? await GetNetworkUsageAsync() : new NetworkUsage(), TopProcesses = _settings.EnableProcessMonitoring ? await GetTopProcessesAsync(_settings.MaxProcessesToTrack) : new List(), Temperature = _settings.EnableTemperatureMonitoring ? await GetTemperatureInfoAsync() : new TemperatureInfo() }; } public async Task GetCpuUsageAsync() { try { var temperature = await GetCpuTemperatureAsync(); return await Task.Run(() => { #pragma warning disable CA1416 // Validate platform compatibility var usage = _counters.TryGetValue("cpu", out var cpuCounter) ? cpuCounter.NextValue() : 0f; // Get per-core usage var coreUsages = new List(); for (int i = 0; i < Environment.ProcessorCount; i++) { try { using var coreCounter = new PerformanceCounter("Processor", "% Processor Time", i.ToString()); coreCounter.NextValue(); Thread.Sleep(100); // Small delay for accurate reading coreUsages.Add(coreCounter.NextValue()); } catch { coreUsages.Add(0f); } } // Get CPU frequency var maxFrequency = 0f; var currentFrequency = 0f; try { using var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_Processor"); using var collection = searcher.Get(); foreach (ManagementObject obj in collection) { maxFrequency = Convert.ToSingle(obj["MaxClockSpeed"]); currentFrequency = Convert.ToSingle(obj["CurrentClockSpeed"]); break; } } catch (Exception ex) { _logger.LogWarning(ex, "Could not get CPU frequency information"); } #pragma warning restore CA1416 // Validate platform compatibility return new CpuUsage { Usage = usage, CoreUsages = coreUsages.ToArray(), MaxFrequency = maxFrequency, CurrentFrequency = currentFrequency, IsThrottling = currentFrequency < maxFrequency * 0.9f, // Consider throttling if running below 90% max frequency Temperature = temperature }; }); } catch (Exception ex) { _logger.LogError(ex, "Error getting CPU usage"); return new CpuUsage(); } } public async Task GetMemoryUsageAsync() { try { return await Task.Run(() => { #pragma warning disable CA1416 // Validate platform compatibility var availableMemoryMB = _counters.TryGetValue("memory_available", out var memCounter) ? memCounter.NextValue() : 0f; var availableMemory = (ulong)(availableMemoryMB * 1024 * 1024); // Get total memory var totalMemory = 0UL; using (var searcher = new ManagementObjectSearcher("SELECT TotalPhysicalMemory FROM Win32_ComputerSystem")) using (var collection = searcher.Get()) { foreach (ManagementObject obj in collection) { totalMemory = (ulong)obj["TotalPhysicalMemory"]; break; } } var usedMemory = totalMemory - availableMemory; var usagePercentage = totalMemory > 0 ? (float)(usedMemory) / totalMemory * 100 : 0f; // Get additional memory info var committedMemory = 0UL; var pagedMemory = 0UL; var nonPagedMemory = 0UL; try { using var osSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_OperatingSystem"); using var osCollection = osSearcher.Get(); foreach (ManagementObject obj in osCollection) { var totalVirtualMemory = (ulong)obj["TotalVirtualMemorySize"] * 1024; var freeVirtualMemory = (ulong)obj["FreeVirtualMemory"] * 1024; committedMemory = totalVirtualMemory - freeVirtualMemory; break; } using var perfSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_PerfRawData_PerfOS_Memory"); using var perfCollection = perfSearcher.Get(); foreach (ManagementObject obj in perfCollection) { pagedMemory = (ulong)obj["PoolPagedBytes"]; nonPagedMemory = (ulong)obj["PoolNonpagedBytes"]; break; } } catch (Exception ex) { _logger.LogWarning(ex, "Could not get extended memory information"); } #pragma warning restore CA1416 // Validate platform compatibility return new MemoryUsage { UsagePercentage = usagePercentage, UsedMemory = usedMemory, AvailableMemory = availableMemory, TotalMemory = totalMemory, CommittedMemory = committedMemory, PagedMemory = pagedMemory, NonPagedMemory = nonPagedMemory }; }); } catch (Exception ex) { _logger.LogError(ex, "Error getting memory usage"); return new MemoryUsage(); } } public async Task GetGpuUsageAsync() { try { return await Task.Run(() => { try { NvmlWrapper.NvmlInit(); IntPtr device; NvmlWrapper.NvmlDeviceGetHandleByIndex(0, out device); NvmlWrapper.NvmlUtilization utilization; NvmlWrapper.NvmlDeviceGetUtilizationRates(device, out utilization); uint temperature; NvmlWrapper.NvmlDeviceGetTemperature(device, 0, out temperature); uint fanSpeed; NvmlWrapper.NvmlDeviceGetFanSpeed(device, out fanSpeed); // Try to get additional information var name = "NVIDIA GPU"; var driverVersion = "Unknown"; var memoryTotal = 0UL; var memoryUsed = 0UL; var powerUsage = 0U; try { // Get GPU name and memory info via WMI as fallback #pragma warning disable CA1416 // Validate platform compatibility using var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_VideoController WHERE Name LIKE '%NVIDIA%'"); using var collection = searcher.Get(); foreach (ManagementObject obj in collection) { name = obj["Name"]?.ToString() ?? name; driverVersion = obj["DriverVersion"]?.ToString() ?? driverVersion; if (obj["AdapterRAM"] != null) { memoryTotal = (ulong)obj["AdapterRAM"]; } break; } #pragma warning restore CA1416 // Validate platform compatibility } catch (Exception ex) { _logger.LogWarning(ex, "Could not get additional GPU information via WMI"); } NvmlWrapper.NvmlShutdown(); return new GpuUsage { Usage = utilization.Gpu, MemoryUsage = utilization.Memory, Temperature = temperature, FanSpeed = fanSpeed, PowerUsage = powerUsage, MemoryTotal = memoryTotal, MemoryUsed = memoryUsed, IsAvailable = true, Name = name, DriverVersion = driverVersion, Error = string.Empty }; } catch (Exception ex) { return new GpuUsage { Usage = 0, MemoryUsage = 0, Temperature = 0, FanSpeed = 0, PowerUsage = 0, MemoryTotal = 0, MemoryUsed = 0, IsAvailable = false, Name = "Unknown", DriverVersion = "Unknown", Error = ex.Message }; } }); } catch (Exception ex) { _logger.LogError(ex, "Error getting GPU usage"); return new GpuUsage { Error = ex.Message }; } } public async Task> GetDiskUsageAsync() { try { return await Task.Run(() => { var diskUsages = new List(); var drives = DriveInfo.GetDrives(); foreach (var drive in drives) { if (drive.IsReady) { var diskUsage = new DiskUsage { DriveLetter = drive.Name, Label = drive.VolumeLabel, FileSystem = drive.DriveFormat, TotalSize = (ulong)drive.TotalSize, FreeSpace = (ulong)drive.AvailableFreeSpace, UsedSpace = (ulong)(drive.TotalSize - drive.AvailableFreeSpace), UsagePercentage = (float)(drive.TotalSize - drive.AvailableFreeSpace) / drive.TotalSize * 100 }; // Get disk performance data try { var diskName = drive.Name.Replace("\\", "").Replace(":", ""); #pragma warning disable CA1416 // Validate platform compatibility using var readCounter = new PerformanceCounter("LogicalDisk", "Disk Read Bytes/sec", diskName); using var writeCounter = new PerformanceCounter("LogicalDisk", "Disk Write Bytes/sec", diskName); using var timeCounter = new PerformanceCounter("LogicalDisk", "% Disk Time", diskName); readCounter.NextValue(); writeCounter.NextValue(); timeCounter.NextValue(); Thread.Sleep(1000); diskUsage.ReadSpeed = readCounter.NextValue() / (1024 * 1024); // Convert to MB/s diskUsage.WriteSpeed = writeCounter.NextValue() / (1024 * 1024); // Convert to MB/s diskUsage.DiskTime = timeCounter.NextValue(); #pragma warning restore CA1416 // Validate platform compatibility } catch (Exception ex) { _logger.LogWarning(ex, "Could not get disk performance data for drive {Drive}", drive.Name); } // Try to determine if it's an SSD try { #pragma warning disable CA1416 // Validate platform compatibility using var searcher = new ManagementObjectSearcher($"SELECT * FROM Win32_LogicalDisk WHERE DeviceID='{drive.Name.TrimEnd('\\')}'"); using var collection = searcher.Get(); foreach (ManagementObject obj in collection) { // This is a simplified check; more sophisticated detection would be needed var mediaType = obj["MediaType"]?.ToString(); diskUsage.IsSSD = mediaType?.Contains("SSD") == true || mediaType?.Contains("Solid") == true; break; } #pragma warning restore CA1416 // Validate platform compatibility } catch (Exception ex) { _logger.LogWarning(ex, "Could not determine disk type for drive {Drive}", drive.Name); } diskUsages.Add(diskUsage); } } return diskUsages; }); } catch (Exception ex) { _logger.LogError(ex, "Error getting disk usage"); return new List(); } } public async Task GetNetworkUsageAsync() { try { return await Task.Run(() => { var networkUsage = new NetworkUsage(); var adapters = new List(); try { #pragma warning disable CA1416 // Validate platform compatibility using var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_PerfRawData_Tcpip_NetworkInterface WHERE Name != 'Loopback'"); using var collection = searcher.Get(); foreach (ManagementObject obj in collection) { var name = obj["Name"]?.ToString() ?? ""; if (name.Contains("Loopback") || name.Contains("Isatap") || name.Contains("Teredo")) continue; var bytesSent = Convert.ToInt64(obj["BytesSentPerSec"] ?? 0); var bytesReceived = Convert.ToInt64(obj["BytesReceivedPerSec"] ?? 0); var timestamp = DateTime.Now; var adapter = new NetworkAdapter { Name = name, IsOperational = true }; // Calculate speeds if we have previous data var key = $"{name}_sent"; if (_previousNetworkBytes.ContainsKey(key) && _previousNetworkTime.ContainsKey(key)) { var timeDiff = (timestamp - _previousNetworkTime[key]).TotalSeconds; if (timeDiff > 0) { var bytesDiff = bytesSent - _previousNetworkBytes[key]; adapter.UploadSpeed = (float)(bytesDiff / timeDiff / (1024 * 1024)); // MB/s networkUsage.UploadSpeed += adapter.UploadSpeed; } } key = $"{name}_received"; if (_previousNetworkBytes.ContainsKey(key) && _previousNetworkTime.ContainsKey(key)) { var timeDiff = (timestamp - _previousNetworkTime[key]).TotalSeconds; if (timeDiff > 0) { var bytesDiff = bytesReceived - _previousNetworkBytes[key]; adapter.DownloadSpeed = (float)(bytesDiff / timeDiff / (1024 * 1024)); // MB/s networkUsage.DownloadSpeed += adapter.DownloadSpeed; } } // Store current values for next calculation _previousNetworkBytes[$"{name}_sent"] = bytesSent; _previousNetworkBytes[$"{name}_received"] = bytesReceived; _previousNetworkTime[$"{name}_sent"] = timestamp; _previousNetworkTime[$"{name}_received"] = timestamp; networkUsage.BytesSent += (ulong)bytesSent; networkUsage.BytesReceived += (ulong)bytesReceived; adapters.Add(adapter); } #pragma warning restore CA1416 // Validate platform compatibility } catch (Exception ex) { _logger.LogWarning(ex, "Could not get network performance data"); } networkUsage.Adapters = adapters; return networkUsage; }); } catch (Exception ex) { _logger.LogError(ex, "Error getting network usage"); return new NetworkUsage(); } } public async Task> GetTopProcessesAsync(int count = 10) { try { return await Task.Run(() => { var processes = Process.GetProcesses() .Where(p => !p.HasExited) .Select(p => { try { return new ProcessInfo { Id = p.Id, Name = p.ProcessName, MemoryUsage = (ulong)p.WorkingSet64, ProcessorTime = p.TotalProcessorTime, StartTime = p.StartTime, ExecutablePath = p.MainModule?.FileName ?? "", CommandLine = GetProcessCommandLine(p.Id) }; } catch { return null; // Skip processes that throw exceptions } }) .Where(p => p != null) .OrderByDescending(p => p!.MemoryUsage) .Take(count) .Cast() .ToList(); return processes; }); } catch (Exception ex) { _logger.LogError(ex, "Error getting top processes"); return new List(); } } private async Task GetCpuTemperatureAsync() { try { return await Task.Run(() => { // Try to get CPU temperature from WMI #pragma warning disable CA1416 // Validate platform compatibility using var searcher = new ManagementObjectSearcher(@"root\WMI", "SELECT * FROM MSAcpi_ThermalZoneTemperature"); using var collection = searcher.Get(); foreach (ManagementObject obj in collection) { var temp = Convert.ToDouble(obj["CurrentTemperature"]); return (float)((temp - 2732) / 10.0); // Convert from tenths of Kelvin to Celsius } #pragma warning restore CA1416 // Validate platform compatibility return 0f; }); } catch (Exception ex) { _logger.LogWarning(ex, "Could not get CPU temperature"); return 0f; } } private async Task GetTemperatureInfoAsync() { try { return await Task.Run(() => { var temperatureInfo = new TemperatureInfo { CPU = GetCpuTemperatureAsync().Result, HardDrives = new List() }; // Try to get hard drive temperatures try { #pragma warning disable CA1416 // Validate platform compatibility using var searcher = new ManagementObjectSearcher(@"root\WMI", "SELECT * FROM MSStorageDriver_ATAPISmartData"); using var collection = searcher.Get(); foreach (ManagementObject obj in collection) { var instanceName = obj["InstanceName"]?.ToString() ?? ""; // This would need more sophisticated parsing for actual SMART data temperatureInfo.HardDrives.Add(new HardDriveTemp { Drive = instanceName, Temperature = 0f, // Would need SMART data parsing Health = "Unknown" }); } #pragma warning restore CA1416 // Validate platform compatibility } catch (Exception ex) { _logger.LogWarning(ex, "Could not get hard drive temperatures"); } return temperatureInfo; }); } catch (Exception ex) { _logger.LogError(ex, "Error getting temperature information"); return new TemperatureInfo(); } } private string GetProcessCommandLine(int processId) { try { #pragma warning disable CA1416 // Validate platform compatibility using var searcher = new ManagementObjectSearcher($"SELECT CommandLine FROM Win32_Process WHERE ProcessId = {processId}"); using var collection = searcher.Get(); foreach (ManagementObject obj in collection) { return obj["CommandLine"]?.ToString() ?? ""; } #pragma warning restore CA1416 // Validate platform compatibility return ""; } catch { return ""; } } public void Dispose() { #pragma warning disable CA1416 // Validate platform compatibility foreach (var counter in _counters.Values) { counter?.Dispose(); } _counters.Clear(); #pragma warning restore CA1416 // Validate platform compatibility } } }