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
This commit is contained in:
@@ -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<Font> 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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,11 @@
|
||||
<!-- 1.6.4: fix UI thread deadlock. Acknowledgment and status checks now run async
|
||||
properly without blocking the UI thread. Acknowledgment is marked locally first,
|
||||
then posted to server in background to prevent lockups. -->
|
||||
<Version>1.6.4</Version>
|
||||
<!-- 1.6.5: fix resource leaks and disposal issues. GraphicsPath properly disposed,
|
||||
Font objects tracked and disposed, worker thread joined before client disposal,
|
||||
Screen.PrimaryScreen null dereference prevented, status check cancellation token
|
||||
propagated to prevent orphaned HTTP requests. -->
|
||||
<Version>1.6.5</Version>
|
||||
<InvariantGlobalization>true</InvariantGlobalization>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
|
||||
@@ -177,7 +177,9 @@
|
||||
No agent code changes, but deployed together for version consistency. -->
|
||||
<!-- 1.13.3: coordinated release with tray 1.6.4 (deadlock fix).
|
||||
No agent code changes, but deployed together for version consistency. -->
|
||||
<Version>1.13.3</Version>
|
||||
<!-- 1.13.4: coordinated release with tray 1.6.5 (resource leak fixes).
|
||||
No agent code changes, but deployed together for version consistency. -->
|
||||
<Version>1.13.4</Version>
|
||||
<InvariantGlobalization>true</InvariantGlobalization>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
|
||||
Reference in New Issue
Block a user