From deb2bbe9df583203fd23e7bb5947d5da614b0a81 Mon Sep 17 00:00:00 2001 From: Proudlock <270015376@geaerospace.com> Date: Tue, 8 Sep 2026 18:22:24 -0400 Subject: [PATCH] Fix resource leaks and disposal issues in fleet alert tray - Dispose GraphicsPath after creating Region to prevent GDI handle leak - Track and dispose all Font objects to prevent GDI+ resource leak - Join worker thread before disposal to ensure client not used after disposal - Fix Screen.PrimaryScreen null dereference with fallback rectangle - Propagate cancellation token to FetchAlertsAsync to stop orphaned requests - Bump tray to 1.6.5, agent to 1.13.4 --- .../FleetAlertForm.cs | 39 ++++++++++++++----- .../FleetAlertWorker.cs | 9 +++++ .../ShopDbEndpointAgent.Tray.csproj | 6 ++- .../ShopDbEndpointAgent.csproj | 4 +- 4 files changed, 47 insertions(+), 11 deletions(-) diff --git a/agent/ShopDbEndpointAgent.Tray/FleetAlertForm.cs b/agent/ShopDbEndpointAgent.Tray/FleetAlertForm.cs index 4b88913d..617a92db 100644 --- a/agent/ShopDbEndpointAgent.Tray/FleetAlertForm.cs +++ b/agent/ShopDbEndpointAgent.Tray/FleetAlertForm.cs @@ -16,8 +16,10 @@ internal sealed class FleetAlertForm : Form private readonly string? hostname; private System.Windows.Forms.Timer? countdownTimer; private System.Windows.Forms.Timer? statusCheckTimer; + private readonly CancellationTokenSource statusCheckCancellation = new(); private int remainingSeconds; private Label? countdownLabel; + private readonly List fonts = new(); public event EventHandler? OnAcknowledgeClicked; @@ -35,7 +37,7 @@ internal sealed class FleetAlertForm : Form ClientSize = new Size(400, 280); BackColor = Color.White; - var screen = Screen.PrimaryScreen?.WorkingArea ?? Screen.PrimaryScreen!.Bounds; + var screen = Screen.PrimaryScreen?.WorkingArea ?? Screen.PrimaryScreen?.Bounds ?? new Rectangle(0, 0, 1920, 1080); Location = new Point(screen.Right - Width - 20, screen.Top + 20); BuildLayout(); @@ -49,6 +51,7 @@ internal sealed class FleetAlertForm : Form { countdownTimer?.Stop(); statusCheckTimer?.Stop(); + statusCheckCancellation.Cancel(); } private void FleetAlertForm_Load(object? sender, EventArgs e) @@ -77,7 +80,7 @@ internal sealed class FleetAlertForm : Form { try { - var alerts = await client.FetchAlertsAsync(hostname, CancellationToken.None) + var alerts = await client.FetchAlertsAsync(hostname, statusCheckCancellation.Token) .ConfigureAwait(false); var stillActive = alerts?.Any(a => a.NotificationId == alert.NotificationId) ?? false; @@ -90,6 +93,10 @@ internal sealed class FleetAlertForm : Form })); } } + catch (OperationCanceledException) + { + // Form is closing, ignore + } catch { // Ignore errors - keep the form open if we can't check status @@ -99,7 +106,7 @@ internal sealed class FleetAlertForm : Form private void ApplyRoundedCorners() { - var path = new GraphicsPath(); + using var path = new GraphicsPath(); int radius = 8; var rect = new Rectangle(0, 0, Width, Height); @@ -146,6 +153,13 @@ internal sealed class FleetAlertForm : Form } } + private Font CreateFont(string family, float size, FontStyle style = FontStyle.Regular) + { + var font = new Font(family, size, style); + fonts.Add(font); + return font; + } + private void BuildLayout() { var borderPanel = new Panel @@ -182,7 +196,7 @@ internal sealed class FleetAlertForm : Form Left = 16, Top = 14, Width = 300, - Font = new Font("Segoe UI", 10, FontStyle.Bold), + Font = CreateFont("Segoe UI", 10, FontStyle.Bold), ForeColor = Color.FromArgb(51, 51, 51), }; headerPanel.Controls.Add(typeLabel); @@ -195,7 +209,7 @@ internal sealed class FleetAlertForm : Form Width = 24, Height = 24, FlatStyle = FlatStyle.Flat, - Font = new Font("Segoe UI", 14), + Font = CreateFont("Segoe UI", 14), ForeColor = Color.FromArgb(102, 102, 102), BackColor = Color.Transparent, Cursor = Cursors.Hand, @@ -212,7 +226,7 @@ internal sealed class FleetAlertForm : Form Top = 58, Width = 360, Height = 50, - Font = new Font("Segoe UI", 11, FontStyle.Bold), + Font = CreateFont("Segoe UI", 11, FontStyle.Bold), ForeColor = Color.FromArgb(51, 51, 51), AutoSize = false, }; @@ -225,7 +239,7 @@ internal sealed class FleetAlertForm : Form Top = 115, Width = 360, Height = 80, - Font = new Font("Segoe UI", 9.75f), + Font = CreateFont("Segoe UI", 9.75f), ForeColor = Color.FromArgb(85, 85, 85), AutoSize = false, }; @@ -241,7 +255,7 @@ internal sealed class FleetAlertForm : Form BackColor = Color.FromArgb(76, 175, 80), ForeColor = Color.White, FlatStyle = FlatStyle.Flat, - Font = new Font("Segoe UI", 9, FontStyle.Bold), + Font = CreateFont("Segoe UI", 9, FontStyle.Bold), Cursor = Cursors.Hand, }; acknowledgeButton.FlatAppearance.BorderSize = 0; @@ -277,7 +291,7 @@ internal sealed class FleetAlertForm : Form Width = 395, Height = 20, TextAlign = ContentAlignment.MiddleCenter, - Font = new Font("Segoe UI", 8.25f), + Font = CreateFont("Segoe UI", 8.25f), ForeColor = Color.FromArgb(153, 153, 153), }; footerPanel.Controls.Add(countdownLabel); @@ -329,6 +343,13 @@ internal sealed class FleetAlertForm : Form countdownTimer?.Dispose(); statusCheckTimer?.Stop(); statusCheckTimer?.Dispose(); + statusCheckCancellation.Cancel(); + statusCheckCancellation.Dispose(); + foreach (var font in fonts) + { + font.Dispose(); + } + fonts.Clear(); } base.Dispose(disposing); } diff --git a/agent/ShopDbEndpointAgent.Tray/FleetAlertWorker.cs b/agent/ShopDbEndpointAgent.Tray/FleetAlertWorker.cs index d9ec6a69..37711abb 100644 --- a/agent/ShopDbEndpointAgent.Tray/FleetAlertWorker.cs +++ b/agent/ShopDbEndpointAgent.Tray/FleetAlertWorker.cs @@ -183,6 +183,15 @@ internal sealed class FleetAlertWorker : IDisposable public void Dispose() { cancellation.Cancel(); + + if (workerThread.IsAlive) + { + if (!workerThread.Join(TimeSpan.FromSeconds(5))) + { + logger.LogWarning("Fleet alert worker thread did not exit within timeout."); + } + } + cancellation.Dispose(); } } diff --git a/agent/ShopDbEndpointAgent.Tray/ShopDbEndpointAgent.Tray.csproj b/agent/ShopDbEndpointAgent.Tray/ShopDbEndpointAgent.Tray.csproj index 4c2e0398..c9223298 100644 --- a/agent/ShopDbEndpointAgent.Tray/ShopDbEndpointAgent.Tray.csproj +++ b/agent/ShopDbEndpointAgent.Tray/ShopDbEndpointAgent.Tray.csproj @@ -82,7 +82,11 @@ - 1.6.4 + + 1.6.5 true win-x64 true diff --git a/agent/ShopDbEndpointAgent/ShopDbEndpointAgent.csproj b/agent/ShopDbEndpointAgent/ShopDbEndpointAgent.csproj index 1400b34b..bf4f7924 100644 --- a/agent/ShopDbEndpointAgent/ShopDbEndpointAgent.csproj +++ b/agent/ShopDbEndpointAgent/ShopDbEndpointAgent.csproj @@ -177,7 +177,9 @@ No agent code changes, but deployed together for version consistency. --> - 1.13.3 + + 1.13.4 true win-x64 true