Skip to content

Commit cd440d3

Browse files
add mutex and restart logic
1 parent b62cef0 commit cd440d3

1 file changed

Lines changed: 152 additions & 5 deletions

File tree

FileSyncAppWin/Program.cs

Lines changed: 152 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,164 @@
1+
using Microsoft.Extensions.Logging;
2+
using System.IO.Pipes;
3+
14
namespace FileSyncAppWin
25
{
36
internal static class Program
47
{
8+
9+
private static Mutex mutex = new Mutex(true, "PraewemaFileSyncAppWin");
10+
private static volatile bool autoRestart = true;
11+
private static ILogger logger;
12+
private static Form mainForm;
13+
private const string PipeName = "PraewemaFileSyncAppWinPipe";
14+
515
/// <summary>
616
/// The main entry point for the application.
717
/// </summary>
818
[STAThread]
9-
static void Main()
19+
static void Main(string[] args)
1020
{
11-
// To customize application configuration such as set high DPI settings or default font,
12-
// see https://aka.ms/applicationconfiguration.
13-
ApplicationConfiguration.Initialize();
14-
Application.Run(new MainForm());
21+
FileSyncApp.Program.ConfigureLogger();
22+
logger = FileSyncApp.Program.LoggerFactory.CreateLogger("FileSyncAppWin");
23+
if (args.Contains("noautorestart"))
24+
{
25+
logger?.LogInformation("startup argument noautorestart given, settings autoRestart to false");
26+
autoRestart = false;
27+
}
28+
if (args.Contains("restart"))
29+
{
30+
logger?.LogInformation("startup argument restart given, sending exit signal to running instance");
31+
SendExitSignalToFirstInstance();
32+
}
33+
logger?.LogInformation("try to get mutex");
34+
// Try to acquire the mutex
35+
if (!mutex.WaitOne(TimeSpan.FromSeconds(15)))
36+
{
37+
// Mutex was not acquired, meaning another instance is running
38+
39+
Console.WriteLine("FileSyncAppWin another instance is running, exiting.");
40+
logger?.LogInformation("FileSyncAppWin another instance is running, exiting.");
41+
return; // Exit the second instance
42+
}
43+
44+
try
45+
{
46+
var pipeServer = new NamedPipeServerStream(PipeName, PipeDirection.InOut);
47+
// Start listening for incoming connections
48+
pipeServer.BeginWaitForConnection(PipeConnectedCallback, pipeServer);
49+
50+
logger?.LogInformation("FileSyncAppWin Main program starting...");
51+
Application.ThreadException += Application_ThreadException;
52+
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
53+
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
54+
ApplicationConfiguration.Initialize();
55+
mainForm = new MainForm();
56+
Application.Run(mainForm);
57+
}
58+
catch (Exception exception)
59+
{
60+
logger?.LogError(exception, "FileSyncAppWin exception in main logic");
61+
}
62+
finally
63+
{
64+
logger?.LogInformation("FileSyncAppWin releasing mutex");
65+
mutex.ReleaseMutex();
66+
}
67+
68+
}
69+
70+
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
71+
{
72+
try
73+
{
74+
logger?.LogCritical((Exception)e.ExceptionObject, "CurrentDomain_UnhandledException isTerminating {A}", e.IsTerminating);
75+
}
76+
catch (Exception castException)
77+
{
78+
logger?.LogCritical(castException, "CurrentDomain_UnhandledException, Exception object {@A}", e.ExceptionObject);
79+
}
80+
//if (e.IsTerminating)
81+
{
82+
RestartApplication();
83+
}
84+
85+
}
86+
87+
private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
88+
{
89+
logger?.LogCritical(e.Exception, "Application_ThreadException");
90+
RestartApplication();
91+
}
92+
93+
private static void PipeConnectedCallback(IAsyncResult result)
94+
{
95+
var pipeServer = (NamedPipeServerStream)result.AsyncState;
96+
97+
try
98+
{
99+
// End waiting for the connection
100+
pipeServer.EndWaitForConnection(result);
101+
102+
// Read the message from the pipe
103+
using (var reader = new StreamReader(pipeServer))
104+
{
105+
string message = reader.ReadLine();
106+
if (message == "Exit")
107+
{
108+
logger?.LogInformation("Received exit signal from second instance. Exiting gracefully.");
109+
mainForm.Close();
110+
//Environment.Exit(0); // Gracefully exit the application
111+
}
112+
}
113+
114+
// Close the pipe
115+
pipeServer.Close();
116+
}
117+
catch (Exception ex)
118+
{
119+
logger?.LogError(ex, "Error handling named pipe connection.");
120+
}
121+
}
122+
123+
private static void SendExitSignalToFirstInstance()
124+
{
125+
try
126+
{
127+
using (var pipeClient = new NamedPipeClientStream(".", PipeName, PipeDirection.Out))
128+
{
129+
pipeClient.Connect(500); // Connect with a timeout
130+
131+
// Send an exit signal
132+
using (var writer = new StreamWriter(pipeClient))
133+
{
134+
writer.WriteLine("Exit");
135+
}
136+
}
137+
}
138+
catch (Exception ex)
139+
{
140+
logger?.LogError(ex, "Error sending exit signal to first instance.");
141+
}
142+
}
143+
144+
145+
private static void RestartApplication()
146+
{
147+
if (autoRestart)
148+
{
149+
// Get the path of the current application
150+
string assemblyPath = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
151+
// Restart the application using the path
152+
logger?.LogInformation("RestartApplication, starting new process {A}", assemblyPath);
153+
System.Diagnostics.Process.Start(assemblyPath);
154+
155+
// Exit the current instance of the application
156+
Environment.Exit(0);
157+
}
158+
else
159+
{
160+
logger?.LogInformation("RestartApplication, skip starting new process -> autoRestart=false");
161+
}
15162
}
16163
}
17164
}

0 commit comments

Comments
 (0)