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 readonly string? hostname;
|
||||||
private System.Windows.Forms.Timer? countdownTimer;
|
private System.Windows.Forms.Timer? countdownTimer;
|
||||||
private System.Windows.Forms.Timer? statusCheckTimer;
|
private System.Windows.Forms.Timer? statusCheckTimer;
|
||||||
|
private readonly CancellationTokenSource statusCheckCancellation = new();
|
||||||
private int remainingSeconds;
|
private int remainingSeconds;
|
||||||
private Label? countdownLabel;
|
private Label? countdownLabel;
|
||||||
|
private readonly List<Font> fonts = new();
|
||||||
|
|
||||||
public event EventHandler? OnAcknowledgeClicked;
|
public event EventHandler? OnAcknowledgeClicked;
|
||||||
|
|
||||||
@@ -35,7 +37,7 @@ internal sealed class FleetAlertForm : Form
|
|||||||
ClientSize = new Size(400, 280);
|
ClientSize = new Size(400, 280);
|
||||||
BackColor = Color.White;
|
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);
|
Location = new Point(screen.Right - Width - 20, screen.Top + 20);
|
||||||
|
|
||||||
BuildLayout();
|
BuildLayout();
|
||||||
@@ -49,6 +51,7 @@ internal sealed class FleetAlertForm : Form
|
|||||||
{
|
{
|
||||||
countdownTimer?.Stop();
|
countdownTimer?.Stop();
|
||||||
statusCheckTimer?.Stop();
|
statusCheckTimer?.Stop();
|
||||||
|
statusCheckCancellation.Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FleetAlertForm_Load(object? sender, EventArgs e)
|
private void FleetAlertForm_Load(object? sender, EventArgs e)
|
||||||
@@ -77,7 +80,7 @@ internal sealed class FleetAlertForm : Form
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var alerts = await client.FetchAlertsAsync(hostname, CancellationToken.None)
|
var alerts = await client.FetchAlertsAsync(hostname, statusCheckCancellation.Token)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
var stillActive = alerts?.Any(a => a.NotificationId == alert.NotificationId) ?? 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
|
catch
|
||||||
{
|
{
|
||||||
// Ignore errors - keep the form open if we can't check status
|
// Ignore errors - keep the form open if we can't check status
|
||||||
@@ -99,7 +106,7 @@ internal sealed class FleetAlertForm : Form
|
|||||||
|
|
||||||
private void ApplyRoundedCorners()
|
private void ApplyRoundedCorners()
|
||||||
{
|
{
|
||||||
var path = new GraphicsPath();
|
using var path = new GraphicsPath();
|
||||||
int radius = 8;
|
int radius = 8;
|
||||||
var rect = new Rectangle(0, 0, Width, Height);
|
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()
|
private void BuildLayout()
|
||||||
{
|
{
|
||||||
var borderPanel = new Panel
|
var borderPanel = new Panel
|
||||||
@@ -182,7 +196,7 @@ internal sealed class FleetAlertForm : Form
|
|||||||
Left = 16,
|
Left = 16,
|
||||||
Top = 14,
|
Top = 14,
|
||||||
Width = 300,
|
Width = 300,
|
||||||
Font = new Font("Segoe UI", 10, FontStyle.Bold),
|
Font = CreateFont("Segoe UI", 10, FontStyle.Bold),
|
||||||
ForeColor = Color.FromArgb(51, 51, 51),
|
ForeColor = Color.FromArgb(51, 51, 51),
|
||||||
};
|
};
|
||||||
headerPanel.Controls.Add(typeLabel);
|
headerPanel.Controls.Add(typeLabel);
|
||||||
@@ -195,7 +209,7 @@ internal sealed class FleetAlertForm : Form
|
|||||||
Width = 24,
|
Width = 24,
|
||||||
Height = 24,
|
Height = 24,
|
||||||
FlatStyle = FlatStyle.Flat,
|
FlatStyle = FlatStyle.Flat,
|
||||||
Font = new Font("Segoe UI", 14),
|
Font = CreateFont("Segoe UI", 14),
|
||||||
ForeColor = Color.FromArgb(102, 102, 102),
|
ForeColor = Color.FromArgb(102, 102, 102),
|
||||||
BackColor = Color.Transparent,
|
BackColor = Color.Transparent,
|
||||||
Cursor = Cursors.Hand,
|
Cursor = Cursors.Hand,
|
||||||
@@ -212,7 +226,7 @@ internal sealed class FleetAlertForm : Form
|
|||||||
Top = 58,
|
Top = 58,
|
||||||
Width = 360,
|
Width = 360,
|
||||||
Height = 50,
|
Height = 50,
|
||||||
Font = new Font("Segoe UI", 11, FontStyle.Bold),
|
Font = CreateFont("Segoe UI", 11, FontStyle.Bold),
|
||||||
ForeColor = Color.FromArgb(51, 51, 51),
|
ForeColor = Color.FromArgb(51, 51, 51),
|
||||||
AutoSize = false,
|
AutoSize = false,
|
||||||
};
|
};
|
||||||
@@ -225,7 +239,7 @@ internal sealed class FleetAlertForm : Form
|
|||||||
Top = 115,
|
Top = 115,
|
||||||
Width = 360,
|
Width = 360,
|
||||||
Height = 80,
|
Height = 80,
|
||||||
Font = new Font("Segoe UI", 9.75f),
|
Font = CreateFont("Segoe UI", 9.75f),
|
||||||
ForeColor = Color.FromArgb(85, 85, 85),
|
ForeColor = Color.FromArgb(85, 85, 85),
|
||||||
AutoSize = false,
|
AutoSize = false,
|
||||||
};
|
};
|
||||||
@@ -241,7 +255,7 @@ internal sealed class FleetAlertForm : Form
|
|||||||
BackColor = Color.FromArgb(76, 175, 80),
|
BackColor = Color.FromArgb(76, 175, 80),
|
||||||
ForeColor = Color.White,
|
ForeColor = Color.White,
|
||||||
FlatStyle = FlatStyle.Flat,
|
FlatStyle = FlatStyle.Flat,
|
||||||
Font = new Font("Segoe UI", 9, FontStyle.Bold),
|
Font = CreateFont("Segoe UI", 9, FontStyle.Bold),
|
||||||
Cursor = Cursors.Hand,
|
Cursor = Cursors.Hand,
|
||||||
};
|
};
|
||||||
acknowledgeButton.FlatAppearance.BorderSize = 0;
|
acknowledgeButton.FlatAppearance.BorderSize = 0;
|
||||||
@@ -277,7 +291,7 @@ internal sealed class FleetAlertForm : Form
|
|||||||
Width = 395,
|
Width = 395,
|
||||||
Height = 20,
|
Height = 20,
|
||||||
TextAlign = ContentAlignment.MiddleCenter,
|
TextAlign = ContentAlignment.MiddleCenter,
|
||||||
Font = new Font("Segoe UI", 8.25f),
|
Font = CreateFont("Segoe UI", 8.25f),
|
||||||
ForeColor = Color.FromArgb(153, 153, 153),
|
ForeColor = Color.FromArgb(153, 153, 153),
|
||||||
};
|
};
|
||||||
footerPanel.Controls.Add(countdownLabel);
|
footerPanel.Controls.Add(countdownLabel);
|
||||||
@@ -329,6 +343,13 @@ internal sealed class FleetAlertForm : Form
|
|||||||
countdownTimer?.Dispose();
|
countdownTimer?.Dispose();
|
||||||
statusCheckTimer?.Stop();
|
statusCheckTimer?.Stop();
|
||||||
statusCheckTimer?.Dispose();
|
statusCheckTimer?.Dispose();
|
||||||
|
statusCheckCancellation.Cancel();
|
||||||
|
statusCheckCancellation.Dispose();
|
||||||
|
foreach (var font in fonts)
|
||||||
|
{
|
||||||
|
font.Dispose();
|
||||||
|
}
|
||||||
|
fonts.Clear();
|
||||||
}
|
}
|
||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,6 +183,15 @@ internal sealed class FleetAlertWorker : IDisposable
|
|||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
cancellation.Cancel();
|
cancellation.Cancel();
|
||||||
|
|
||||||
|
if (workerThread.IsAlive)
|
||||||
|
{
|
||||||
|
if (!workerThread.Join(TimeSpan.FromSeconds(5)))
|
||||||
|
{
|
||||||
|
logger.LogWarning("Fleet alert worker thread did not exit within timeout.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cancellation.Dispose();
|
cancellation.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,7 +82,11 @@
|
|||||||
<!-- 1.6.4: fix UI thread deadlock. Acknowledgment and status checks now run async
|
<!-- 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,
|
properly without blocking the UI thread. Acknowledgment is marked locally first,
|
||||||
then posted to server in background to prevent lockups. -->
|
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>
|
<InvariantGlobalization>true</InvariantGlobalization>
|
||||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
<SelfContained>true</SelfContained>
|
<SelfContained>true</SelfContained>
|
||||||
|
|||||||
@@ -177,7 +177,9 @@
|
|||||||
No agent code changes, but deployed together for version consistency. -->
|
No agent code changes, but deployed together for version consistency. -->
|
||||||
<!-- 1.13.3: coordinated release with tray 1.6.4 (deadlock fix).
|
<!-- 1.13.3: coordinated release with tray 1.6.4 (deadlock fix).
|
||||||
No agent code changes, but deployed together for version consistency. -->
|
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>
|
<InvariantGlobalization>true</InvariantGlobalization>
|
||||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
<SelfContained>true</SelfContained>
|
<SelfContained>true</SelfContained>
|
||||||
|
|||||||
Reference in New Issue
Block a user