From 9f89388468b88ceead3aed519ebc1c8956105f0f Mon Sep 17 00:00:00 2001 From: laurentiu021 Date: Fri, 29 May 2026 15:49:54 +0300 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20audit=20findings=20=E2=80=94=20memor?= =?UTF-8?q?y=20leaks,=20silent=20catches,=20UI=20flicker,=20consistency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AboutViewModel: dispose ManagementObject in all 5 WMI loops - ThemeService: log errors instead of empty catch blocks - DashboardViewModel: replace bare catch(Exception) with logged exceptions - BulkInstallerViewModel: FilteredApps → BulkObservableCollection - ShortcutCleanerView: DataGrid Background/BorderThickness fix --- CHANGELOG.md | 9 +++++++++ SysManager/SysManager/Services/ThemeService.cs | 5 +++-- SysManager/SysManager/SysManager.csproj | 6 +++--- SysManager/SysManager/ViewModels/AboutViewModel.cs | 5 +++++ .../SysManager/ViewModels/BulkInstallerViewModel.cs | 6 ++---- SysManager/SysManager/ViewModels/DashboardViewModel.cs | 10 +++++----- SysManager/SysManager/Views/ShortcutCleanerView.xaml | 4 ++-- 7 files changed, 29 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ddfdfa2..67cf250 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [1.17.2] - 2026-05-29 + +### Fixed +- **Memory leaks** — AboutViewModel now properly disposes ManagementObject instances in all 5 WMI foreach loops (CPU, RAM, GPU, Display, OS detection). +- **Silent failures** — ThemeService Save/Load empty catch blocks now log errors via Serilog instead of swallowing silently. +- **Dashboard error handling** — replaced 4 bare `catch (Exception)` in alert scanners with logged exceptions for diagnostics. +- **UI flicker** — BulkInstallerViewModel.FilteredApps converted from ObservableCollection to BulkObservableCollection with ReplaceWith(). +- **Visual consistency** — ShortcutCleanerView DataGrid now has `Background="Transparent"` and `BorderThickness="0"` matching all other views. + ## [1.17.1] - 2026-05-29 ### Fixed diff --git a/SysManager/SysManager/Services/ThemeService.cs b/SysManager/SysManager/Services/ThemeService.cs index 7f5104a..6326367 100644 --- a/SysManager/SysManager/Services/ThemeService.cs +++ b/SysManager/SysManager/Services/ThemeService.cs @@ -6,6 +6,7 @@ using System.Text.Json; using System.Windows; using System.Windows.Media; +using Serilog; namespace SysManager.Services; @@ -216,7 +217,7 @@ private void Save() var json = JsonSerializer.Serialize(data, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(SettingsPath, json); } - catch { } + catch (Exception ex) { Log.Debug("Theme save failed: {Error}", ex.Message); } } private void Load() @@ -247,7 +248,7 @@ private void Load() ApplyShade(); } } - catch { } + catch (Exception ex) { Log.Debug("Theme load failed: {Error}", ex.Message); } } private sealed record ThemeSettings( diff --git a/SysManager/SysManager/SysManager.csproj b/SysManager/SysManager/SysManager.csproj index 9ec8279..350dc2e 100644 --- a/SysManager/SysManager/SysManager.csproj +++ b/SysManager/SysManager/SysManager.csproj @@ -10,9 +10,9 @@ SysManager true NU1603;NU1701 - 1.17.1 - 1.17.1.0 - 1.17.1.0 + 1.17.2 + 1.17.2.0 + 1.17.2.0 SysManager SysManager — Windows system monitoring toolkit by laurentiu021. Network, updates, health, logs, safe deep cleanup. https://github.com/laurentiu021/SystemManager diff --git a/SysManager/SysManager/ViewModels/AboutViewModel.cs b/SysManager/SysManager/ViewModels/AboutViewModel.cs index 65a3f8f..69432fa 100644 --- a/SysManager/SysManager/ViewModels/AboutViewModel.cs +++ b/SysManager/SysManager/ViewModels/AboutViewModel.cs @@ -278,6 +278,7 @@ private string CollectEnvironmentInfo() using var cpuSearch = new System.Management.ManagementObjectSearcher( "SELECT Name,NumberOfCores,NumberOfLogicalProcessors,MaxClockSpeed FROM Win32_Processor"); foreach (System.Management.ManagementObject mo in cpuSearch.Get()) + using (mo) { var name = mo["Name"]?.ToString()?.Trim() ?? "unknown"; var cores = mo["NumberOfCores"]; @@ -298,6 +299,7 @@ private string CollectEnvironmentInfo() using var memSearch = new System.Management.ManagementObjectSearcher( "SELECT TotalVisibleMemorySize,FreePhysicalMemory FROM Win32_OperatingSystem"); foreach (System.Management.ManagementObject mo in memSearch.Get()) + using (mo) { var totalKb = mo["TotalVisibleMemorySize"] as ulong? ?? 0; var freeKb = mo["FreePhysicalMemory"] as ulong? ?? 0; @@ -314,6 +316,7 @@ private string CollectEnvironmentInfo() using var gpuSearch = new System.Management.ManagementObjectSearcher( "SELECT Name,DriverVersion,AdapterRAM FROM Win32_VideoController"); foreach (System.Management.ManagementObject mo in gpuSearch.Get()) + using (mo) { var name = mo["Name"]?.ToString()?.Trim() ?? "unknown"; var driver = mo["DriverVersion"]?.ToString() ?? ""; @@ -341,6 +344,7 @@ private string CollectEnvironmentInfo() using var dispSearch = new System.Management.ManagementObjectSearcher( "SELECT CurrentHorizontalResolution,CurrentVerticalResolution,CurrentRefreshRate FROM Win32_VideoController"); foreach (System.Management.ManagementObject mo in dispSearch.Get()) + using (mo) { var w = mo["CurrentHorizontalResolution"]; var h = mo["CurrentVerticalResolution"]; @@ -377,6 +381,7 @@ private static string DescribeWindows() using var searcher = new System.Management.ManagementObjectSearcher( "SELECT Caption,BuildNumber FROM Win32_OperatingSystem"); foreach (System.Management.ManagementObject mo in searcher.Get()) + using (mo) { var caption = mo["Caption"]?.ToString()?.Trim() ?? ""; var build = mo["BuildNumber"]?.ToString() ?? ""; diff --git a/SysManager/SysManager/ViewModels/BulkInstallerViewModel.cs b/SysManager/SysManager/ViewModels/BulkInstallerViewModel.cs index e7b1d14..617a894 100644 --- a/SysManager/SysManager/ViewModels/BulkInstallerViewModel.cs +++ b/SysManager/SysManager/ViewModels/BulkInstallerViewModel.cs @@ -25,7 +25,7 @@ public sealed partial class BulkInstallerViewModel : ViewModelBase private CancellationTokenSource? _cts; public BulkObservableCollection Apps { get; } = new(); - public ObservableCollection FilteredApps { get; } = new(); + public BulkObservableCollection FilteredApps { get; } = new(); public ICollectionView GroupedView { get; } [ObservableProperty] private string _filterText = ""; @@ -344,9 +344,7 @@ private void ApplyFilter() filtered = filtered.Where(a => a.Name.Contains(FilterText, StringComparison.OrdinalIgnoreCase)); - FilteredApps.Clear(); - foreach (var app in filtered) - FilteredApps.Add(app); + FilteredApps.ReplaceWith(filtered); } private static string GlyphForCategory(string category) => category switch diff --git a/SysManager/SysManager/ViewModels/DashboardViewModel.cs b/SysManager/SysManager/ViewModels/DashboardViewModel.cs index 3c115ee..3c2687c 100644 --- a/SysManager/SysManager/ViewModels/DashboardViewModel.cs +++ b/SysManager/SysManager/ViewModels/DashboardViewModel.cs @@ -173,9 +173,9 @@ private void UpdateGpuUsage() }); } } - catch (Exception) + catch (Exception ex) { - // No NVIDIA or driver issue — GPU stays at default values + Log.Debug("GPU polling unavailable: {Error}", ex.Message); } } @@ -370,7 +370,7 @@ await runner.RunScriptViaPwshAsync( } }); } - catch (Exception) + catch (Exception ex) { Log.Debug("Alert scan failed: {Error}", ex.Message); }// { System.Windows.Application.Current?.Dispatcher.BeginInvoke(() => { @@ -425,7 +425,7 @@ private Task ScanEventLogAsync(DashboardAlert alert) } }); } - catch (Exception) + catch (Exception ex) { Log.Debug("Alert scan failed: {Error}", ex.Message); }// { System.Windows.Application.Current?.Dispatcher.BeginInvoke(() => { @@ -458,7 +458,7 @@ private Task ScanWindowsFeaturesAsync(DashboardAlert alert) } }); } - catch (Exception) + catch (Exception ex) { Log.Debug("Alert scan failed: {Error}", ex.Message); }// { System.Windows.Application.Current?.Dispatcher.BeginInvoke(() => { diff --git a/SysManager/SysManager/Views/ShortcutCleanerView.xaml b/SysManager/SysManager/Views/ShortcutCleanerView.xaml index 3a40a22..9ba9ff1 100644 --- a/SysManager/SysManager/Views/ShortcutCleanerView.xaml +++ b/SysManager/SysManager/Views/ShortcutCleanerView.xaml @@ -56,9 +56,9 @@ From ff325351ae0c68274e4426a15eef65bd9f810b3e Mon Sep 17 00:00:00 2001 From: laurentiu021 Date: Fri, 29 May 2026 16:35:44 +0300 Subject: [PATCH 2/2] style: fix whitespace formatting in AboutViewModel --- .../SysManager/ViewModels/AboutViewModel.cs | 96 +++++++++---------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/SysManager/SysManager/ViewModels/AboutViewModel.cs b/SysManager/SysManager/ViewModels/AboutViewModel.cs index 69432fa..5f2c7e3 100644 --- a/SysManager/SysManager/ViewModels/AboutViewModel.cs +++ b/SysManager/SysManager/ViewModels/AboutViewModel.cs @@ -278,18 +278,18 @@ private string CollectEnvironmentInfo() using var cpuSearch = new System.Management.ManagementObjectSearcher( "SELECT Name,NumberOfCores,NumberOfLogicalProcessors,MaxClockSpeed FROM Win32_Processor"); foreach (System.Management.ManagementObject mo in cpuSearch.Get()) - using (mo) - { - var name = mo["Name"]?.ToString()?.Trim() ?? "unknown"; - var cores = mo["NumberOfCores"]; - var threads = mo["NumberOfLogicalProcessors"]; - var mhz = mo["MaxClockSpeed"]; - sb.Append("CPU: ").Append(name); - if (cores is not null) sb.Append($" ({cores}c/{threads}t)"); - if (mhz is uint speed) sb.Append($" @ {speed / 1000.0:F1} GHz"); - sb.AppendLine(); - break; - } + using (mo) + { + var name = mo["Name"]?.ToString()?.Trim() ?? "unknown"; + var cores = mo["NumberOfCores"]; + var threads = mo["NumberOfLogicalProcessors"]; + var mhz = mo["MaxClockSpeed"]; + sb.Append("CPU: ").Append(name); + if (cores is not null) sb.Append($" ({cores}c/{threads}t)"); + if (mhz is uint speed) sb.Append($" @ {speed / 1000.0:F1} GHz"); + sb.AppendLine(); + break; + } } catch (System.Management.ManagementException ex) { Log.Debug("CPU info unavailable: {Error}", ex.Message); } @@ -299,14 +299,14 @@ private string CollectEnvironmentInfo() using var memSearch = new System.Management.ManagementObjectSearcher( "SELECT TotalVisibleMemorySize,FreePhysicalMemory FROM Win32_OperatingSystem"); foreach (System.Management.ManagementObject mo in memSearch.Get()) - using (mo) - { - var totalKb = mo["TotalVisibleMemorySize"] as ulong? ?? 0; - var freeKb = mo["FreePhysicalMemory"] as ulong? ?? 0; - if (totalKb > 0) - sb.AppendLine($"RAM: {totalKb / 1024.0 / 1024.0:F1} GB total, {freeKb / 1024.0 / 1024.0:F1} GB free"); - break; - } + using (mo) + { + var totalKb = mo["TotalVisibleMemorySize"] as ulong? ?? 0; + var freeKb = mo["FreePhysicalMemory"] as ulong? ?? 0; + if (totalKb > 0) + sb.AppendLine($"RAM: {totalKb / 1024.0 / 1024.0:F1} GB total, {freeKb / 1024.0 / 1024.0:F1} GB free"); + break; + } } catch (System.Management.ManagementException ex) { Log.Debug("RAM info unavailable: {Error}", ex.Message); } @@ -316,16 +316,16 @@ private string CollectEnvironmentInfo() using var gpuSearch = new System.Management.ManagementObjectSearcher( "SELECT Name,DriverVersion,AdapterRAM FROM Win32_VideoController"); foreach (System.Management.ManagementObject mo in gpuSearch.Get()) - using (mo) - { - var name = mo["Name"]?.ToString()?.Trim() ?? "unknown"; - var driver = mo["DriverVersion"]?.ToString() ?? ""; - var vram = mo["AdapterRAM"] as uint? ?? 0; - sb.Append("GPU: ").Append(name); - if (vram > 0) sb.Append($" ({vram / 1024.0 / 1024.0 / 1024.0:F1} GB VRAM)"); - if (!string.IsNullOrEmpty(driver)) sb.Append($" driver {driver}"); - sb.AppendLine(); - } + using (mo) + { + var name = mo["Name"]?.ToString()?.Trim() ?? "unknown"; + var driver = mo["DriverVersion"]?.ToString() ?? ""; + var vram = mo["AdapterRAM"] as uint? ?? 0; + sb.Append("GPU: ").Append(name); + if (vram > 0) sb.Append($" ({vram / 1024.0 / 1024.0 / 1024.0:F1} GB VRAM)"); + if (!string.IsNullOrEmpty(driver)) sb.Append($" driver {driver}"); + sb.AppendLine(); + } } catch (System.Management.ManagementException ex) { Log.Debug("GPU info unavailable: {Error}", ex.Message); } @@ -344,19 +344,19 @@ private string CollectEnvironmentInfo() using var dispSearch = new System.Management.ManagementObjectSearcher( "SELECT CurrentHorizontalResolution,CurrentVerticalResolution,CurrentRefreshRate FROM Win32_VideoController"); foreach (System.Management.ManagementObject mo in dispSearch.Get()) - using (mo) - { - var w = mo["CurrentHorizontalResolution"]; - var h = mo["CurrentVerticalResolution"]; - var hz = mo["CurrentRefreshRate"]; - if (w is not null && h is not null) + using (mo) { - sb.Append($"Display: {w}×{h}"); - if (hz is not null) sb.Append($" @ {hz} Hz"); - sb.AppendLine(); - break; + var w = mo["CurrentHorizontalResolution"]; + var h = mo["CurrentVerticalResolution"]; + var hz = mo["CurrentRefreshRate"]; + if (w is not null && h is not null) + { + sb.Append($"Display: {w}×{h}"); + if (hz is not null) sb.Append($" @ {hz} Hz"); + sb.AppendLine(); + break; + } } - } } catch (System.Management.ManagementException ex) { Log.Debug("Display info unavailable: {Error}", ex.Message); } @@ -381,13 +381,13 @@ private static string DescribeWindows() using var searcher = new System.Management.ManagementObjectSearcher( "SELECT Caption,BuildNumber FROM Win32_OperatingSystem"); foreach (System.Management.ManagementObject mo in searcher.Get()) - using (mo) - { - var caption = mo["Caption"]?.ToString()?.Trim() ?? ""; - var build = mo["BuildNumber"]?.ToString() ?? ""; - if (!string.IsNullOrEmpty(caption)) - return $"{caption} (build {build})"; - } + using (mo) + { + var caption = mo["Caption"]?.ToString()?.Trim() ?? ""; + var build = mo["BuildNumber"]?.ToString() ?? ""; + if (!string.IsNullOrEmpty(caption)) + return $"{caption} (build {build})"; + } } catch (System.Management.ManagementException ex) { Log.Debug("WMI OS info unavailable: {Error}", ex.Message); }