feat: 实现单实例应用程序支持
This commit is contained in:
17
MainForm.cs
17
MainForm.cs
@@ -572,6 +572,23 @@ namespace TimerApp
|
||||
this.Activate();
|
||||
}
|
||||
|
||||
public void ActivateFromExternal()
|
||||
{
|
||||
if (InvokeRequired)
|
||||
{
|
||||
BeginInvoke(new Action(ActivateFromExternal));
|
||||
return;
|
||||
}
|
||||
|
||||
this.Show();
|
||||
this.WindowState = FormWindowState.Normal;
|
||||
this.ShowInTaskbar = true;
|
||||
this.BringToFront();
|
||||
this.Activate();
|
||||
this.TopMost = true;
|
||||
this.TopMost = false;
|
||||
}
|
||||
|
||||
private void toolStripMenuItemExit_Click(object sender, EventArgs e)
|
||||
{
|
||||
_monitor.Stop();
|
||||
|
||||
22
Program.cs
22
Program.cs
@@ -8,11 +8,21 @@ static class Program
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
TaskbarIntegration.InitializeProcess();
|
||||
// To customize application configuration such as set high DPI settings or default font,
|
||||
// see https://aka.ms/applicationconfiguration.
|
||||
ApplicationConfiguration.Initialize();
|
||||
TaskbarIntegration.InitializeShortcuts();
|
||||
Application.Run(new MainForm());
|
||||
if (!SingleInstanceManager.TryAcquire(out var instance) || instance is null)
|
||||
{
|
||||
SingleInstanceManager.SignalExistingInstance();
|
||||
return;
|
||||
}
|
||||
|
||||
using (instance)
|
||||
{
|
||||
TaskbarIntegration.InitializeProcess();
|
||||
ApplicationConfiguration.Initialize();
|
||||
TaskbarIntegration.InitializeShortcuts();
|
||||
|
||||
var mainForm = new MainForm();
|
||||
instance.StartServer(mainForm.ActivateFromExternal);
|
||||
Application.Run(mainForm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
118
SingleInstanceManager.cs
Normal file
118
SingleInstanceManager.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user