Недавно я решил перенести одно из своих настольных приложений WPF для Windows, написанное на C# и ориентированное на .NET Framework 4.5, на новейшую версию .NET Core 3.1. Все было хорошо, пока мне не пришлось добавить поддержку приложения с одним экземпляром, при этом имея возможность передавать любые параметры из второго экземпляра в первый запущенный экземпляр. Моя предыдущая реализация WPF для приложения с одним экземпляром использовала System.Runtime.Remoting, которая недоступна в .NET Core. Поэтому мне пришлось сделать что-то новое. Ниже приведена реализация, которую я придумал. Он отлично работает, но я чувствую, что его можно улучшить. Пожалуйста, не стесняйтесь обсуждать и улучшать предложенное решение.
Я создал SingleInstanceService, который использует семафор, чтобы сигнализировать, является ли он первым экземпляром или нет. Если это первый экземпляр, я создаю TcpListener и неопределенно долго жду любых аргументов, переданных из второго экземпляра. Если запущен второй экземпляр, то я отправляю параметры второго экземпляра в первый слушающий экземпляр, а затем выхожу из второго экземпляра.
internal class SingleInstanceService
{
internal Action<string[]> OnArgumentsReceived;
internal bool IsFirstInstance()
{
if (Semaphore.TryOpenExisting(semaphoreName, out semaphore))
{
Task.Run(() => { SendArguments(); Environment.Exit(0); });
return false;
}
else
{
semaphore = new Semaphore(0, 1, semaphoreName);
Task.Run(() => ListenForArguments());
return true;
}
}
private void ListenForArguments()
{
TcpListener tcpListener = new TcpListener(IPAddress.Parse(localHost), localPort);
try
{
tcpListener.Start();
while (true)
{
TcpClient client = tcpListener.AcceptTcpClient();
Task.Run(() => ReceivedMessage(client));
}
}
catch (SocketException ex)
{
Log.Error(ex);
tcpListener.Stop();
}
}
private void ReceivedMessage(TcpClient tcpClient)
{
try
{
using (NetworkStream networkStream = tcpClient?.GetStream())
{
string data = null;
byte[] bytes = new byte[256];
int bytesCount;
while ((bytesCount = networkStream.Read(bytes, 0, bytes.Length)) != 0)
{
data += Encoding.UTF8.GetString(bytes, 0, bytesCount);
}
OnArgumentsReceived(data.Split(' '));
}
}
catch (Exception ex)
{
Log.Error(ex);
}
}
private void SendArguments()
{
try
{
using (TcpClient tcpClient = new TcpClient(localHost, localPort))
{
using (NetworkStream networkStream = tcpClient.GetStream())
{
byte[] data = Encoding.UTF8.GetBytes(string.Join(" ", Environment.GetCommandLineArgs()));
networkStream.Write(data, 0, data.Length);
}
}
}
catch (Exception ex)
{
Log.Error(ex);
}
}
private Semaphore semaphore;
private string semaphoreName = $"Global\\{Environment.MachineName}-myAppName{Assembly.GetExecutingAssembly().GetName().Version}-sid{Process.GetCurrentProcess().SessionId}";
private string localHost = "127.0.0.1";
private int localPort = 19191;
}
Затем в App.xaml.cs у меня есть следующий код:
protected override void OnStartup(StartupEventArgs e)
{
SingleInstanceService singleInstanceService = new SingleInstanceService();
if (singleInstanceService.IsFirstInstance())
{
singleInstanceService.OnArgumentsReceived += OnArgumentsReceived;
// Some other calls
}
base.OnStartup(e);
}
private void OnArgumentsReceived(string[] args)
{
// ProcessCommandLineArgs(args);
}
Пожалуйста, не стесняйтесь обсуждать эту тему, которая, я думаю, является очень распространенным вариантом использования среди разработчиков настольных компьютеров Windows. Спасибо.
Mutex
, он доступен в ядре .NET. Какие аргументы вы хотите передать второму экземпляру? - person Pavel Anikhouski   schedule 12.01.2020