Files
TimerApp/SingleInstanceManager.cs

119 lines
3.2 KiB
C#

using System;
using System.IO;
using System.IO.Pipes;
using System.Threading;
using System.Threading.Tasks;
namespace TimerApp
{
internal sealed class SingleInstanceManager : IDisposable
{
private const string MutexName = "Local\\TimerApp.SingleInstance";
private const string PipeName = "TimerApp.SingleInstancePipe";
private readonly Mutex _mutex;
private readonly CancellationTokenSource _cts = new();
private SingleInstanceManager(Mutex mutex)
{
_mutex = mutex;
}
public static bool TryAcquire(out SingleInstanceManager? manager)
{
manager = null;
try
{
var mutex = new Mutex(true, MutexName, out bool createdNew);
if (!createdNew)
{
mutex.Dispose();
return false;
}
manager = new SingleInstanceManager(mutex);
return true;
}
catch
{
return true;
}
}
public static void SignalExistingInstance()
{
try
{
using var client = new NamedPipeClientStream(".", PipeName, PipeDirection.Out);
client.Connect(200);
using var writer = new StreamWriter(client) { AutoFlush = true };
writer.WriteLine("show");
}
catch
{
}
}
public void StartServer(Action onShowRequested)
{
Task.Run(() => ServerLoop(onShowRequested, _cts.Token));
}
private static async Task ServerLoop(Action onShowRequested, CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
try
{
using var server = new NamedPipeServerStream(
PipeName,
PipeDirection.In,
1,
PipeTransmissionMode.Byte,
PipeOptions.Asynchronous
);
await server.WaitForConnectionAsync(cancellationToken).ConfigureAwait(false);
using var reader = new StreamReader(server);
string? line = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false);
if (string.Equals(line, "show", StringComparison.OrdinalIgnoreCase))
onShowRequested();
}
catch (OperationCanceledException)
{
return;
}
catch
{
await Task.Delay(150, cancellationToken).ConfigureAwait(false);
}
}
}
public void Dispose()
{
try
{
_cts.Cancel();
}
catch
{
}
try
{
_mutex.ReleaseMutex();
}
catch
{
}
_mutex.Dispose();
_cts.Dispose();
}
}
}