From 40aa11d2a958f47acccb78ae2aaa6e7feea98e91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=E2=96=88=E2=96=88=E2=96=88=E2=96=88=E2=96=88?= Date: Wed, 23 Jul 2025 13:54:14 -0400 Subject: [PATCH] feat: single instance of application --- src/TwitchStreamingTools/Program.cs | 40 +++++++++++++++---- .../TwitchStreamingTools.csproj.DotSettings | 3 +- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/TwitchStreamingTools/Program.cs b/src/TwitchStreamingTools/Program.cs index f7e7589..d0b296d 100644 --- a/src/TwitchStreamingTools/Program.cs +++ b/src/TwitchStreamingTools/Program.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Runtime.Versioning; +using System.Threading; using Avalonia; using Avalonia.ReactiveUI; @@ -13,14 +14,24 @@ namespace TwitchStreamingTools; internal sealed class Program { + // Initialization code. Don't use any Avalonia, third-party APIs or any + // SynchronizationContext-reliant code before AppMain is called: things aren't initialized + // yet and stuff might break. + + /// + /// The name used to ensure only a single instance of the application is running. + /// + private const string MutexName = "TwitchStreamingTools_SingleInstance"; + /// /// The logger. /// private static readonly ILog Log = LogManager.GetLogger(typeof(Program)); - // Initialization code. Don't use any Avalonia, third-party APIs or any - // SynchronizationContext-reliant code before AppMain is called: things aren't initialized - // yet and stuff might break. + /// + /// Used to prevent multiple instances of the application from running. + /// + private static Mutex? _mutex; /// /// Main entrypoint of the application. @@ -31,7 +42,7 @@ public static void Main(string[] args) { #if DEBUG XmlConfigurator.Configure(new FileInfo("log4net.debug.config")); #else - XmlConfigurator.Configure(new FileInfo("log4net.config")); + XmlConfigurator.Configure(new FileInfo("log4net.config")); #endif Log.Info("Started application"); @@ -40,11 +51,24 @@ public static void Main(string[] args) { Log.Fatal("Unhandled exception", exceptArgs.ExceptionObject as Exception); }; - BuildAvaloniaApp() - .StartWithClassicDesktopLifetime(args); - } + // We only allow a single instance of the application to be launched at once (due to having only a single config + // file) + _mutex = new Mutex(true, MutexName, out bool onlyAppInstance); + if (!onlyAppInstance) { + Log.Info("Application instance already running. Exiting."); + return; + } - // Avalonia configuration, don't remove; also used by visual designer. + try { + BuildAvaloniaApp() + .StartWithClassicDesktopLifetime(args); + } + finally { + // Release the mutex when the application exits + _mutex.ReleaseMutex(); + _mutex.Dispose(); + } + } /// /// Builds the avalonia application. diff --git a/src/TwitchStreamingTools/TwitchStreamingTools.csproj.DotSettings b/src/TwitchStreamingTools/TwitchStreamingTools.csproj.DotSettings index 4527ed0..d0d789b 100644 --- a/src/TwitchStreamingTools/TwitchStreamingTools.csproj.DotSettings +++ b/src/TwitchStreamingTools/TwitchStreamingTools.csproj.DotSettings @@ -1,4 +1,5 @@ - Library \ No newline at end of file