From d421b0048288d737b8f499eb788f1f6696eb99e6 Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Thu, 19 Feb 2026 13:51:41 -0500 Subject: [PATCH] Fix mutex crash on app exit (fixes #89) ReleaseMutex() throws ApplicationException if called by a thread that doesn't own the mutex. This happens when the app exits after detecting a second instance, or when shutdown is triggered from a non-owning thread. Track ownership and only release if we own it. Fixed in both Dashboard and Lite. Co-Authored-By: Claude Opus 4.6 --- Dashboard/App.xaml.cs | 11 +++++++---- Lite/App.xaml.cs | 11 +++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Dashboard/App.xaml.cs b/Dashboard/App.xaml.cs index 1a17661f..54af8047 100644 --- a/Dashboard/App.xaml.cs +++ b/Dashboard/App.xaml.cs @@ -20,13 +20,14 @@ public partial class App : Application { private const string MutexName = "PerformanceMonitorDashboard_SingleInstance"; private Mutex? _singleInstanceMutex; + private bool _ownsMutex; protected override void OnStartup(StartupEventArgs e) { // Check for existing instance - _singleInstanceMutex = new Mutex(true, MutexName, out bool isNewInstance); + _singleInstanceMutex = new Mutex(true, MutexName, out _ownsMutex); - if (!isNewInstance) + if (!_ownsMutex) { // Another instance is already running - activate it and exit NativeMethods.BroadcastShowMessage(); @@ -58,8 +59,10 @@ protected override void OnExit(ExitEventArgs e) mainWin.ExitApplication(); } - // Release the mutex - _singleInstanceMutex?.ReleaseMutex(); + if (_ownsMutex) + { + _singleInstanceMutex?.ReleaseMutex(); + } _singleInstanceMutex?.Dispose(); base.OnExit(e); diff --git a/Lite/App.xaml.cs b/Lite/App.xaml.cs index 47d46db6..950855c7 100644 --- a/Lite/App.xaml.cs +++ b/Lite/App.xaml.cs @@ -20,6 +20,7 @@ public partial class App : Application { private const string MutexName = "PerformanceMonitorLite_SingleInstance"; private Mutex? _singleInstanceMutex; + private bool _ownsMutex; /// /// Gets the application data directory where config and data files are stored. @@ -119,9 +120,9 @@ protected override void OnStartup(StartupEventArgs e) { // Check for existing instance - _singleInstanceMutex = new Mutex(true, MutexName, out bool isNewInstance); + _singleInstanceMutex = new Mutex(true, MutexName, out _ownsMutex); - if (!isNewInstance) + if (!_ownsMutex) { MessageBox.Show( "Performance Monitor Lite is already running.", @@ -168,8 +169,10 @@ protected override void OnExit(ExitEventArgs e) AppLogger.Info("App", "Shutting down"); AppLogger.Shutdown(); - // Release the mutex - _singleInstanceMutex?.ReleaseMutex(); + if (_ownsMutex) + { + _singleInstanceMutex?.ReleaseMutex(); + } _singleInstanceMutex?.Dispose(); base.OnExit(e);