C#点网核心单实例应用程序将参数传递给第一个实例

问题描述 投票:4回答:1

最近,我决定迁移用C#编写的WPF Windows桌面应用程序之一,并将。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桌面开发人员中非常普遍的用例。谢谢。

c# .net-core parameter-passing single-instance
1个回答
0
投票

使用依赖项注入。您将需要定义一个接口,然后实现该接口。在启动代码中,您将需要添加接口及其作为单例服务的实现。

在实现构造函数中,您可以只将要运行的代码放入一次。这将使对象的生存期保持不变。

还有其他类型的注入,瞬态和范围注入,但对于您的用例,您可能只需要单例。

Program.cs

using System;
using Microsoft.Extensions.DependencyInjection;

namespace example
{
    class Program
    {
        static void Main(string[] args)
        {
            // create service collection
            var serviceCollection = new ServiceCollection();
            ConfigureServices(serviceCollection);

            var serviceProvider = serviceCollection.BuildServiceProvider();

            serviceProvider.GetService<Startup>().Run();
        }

        private static void ConfigureServices(IServiceCollection serviceCollection)
        {
            // add services
            serviceCollection.AddTransient<IMyService, MyService>();

            serviceCollection.AddTransient<Startup>();
        }
    }
}

Startup.cs

namespace example
{
    public class Startup
    {
        private readonly IMyService _myService;
        public Startup(IMyService myService)
        {
            _myService = myService;
        }

        public void Run()
        {
            _myService.MyMethod();
        }

    }
}

Interace

namespace example
{
    public interface IMyService
    {
        void MyMethod();
    }
}

服务的实施

 namespace example
    {
        public class MyService : IMyService
        {
            public MyService()
            {
                // on signleton service this only gets invoked once
            }

            public void MyMethod()
            {
                // your imolmentation here 
            }
        }
    }

一些在线参考:https://dzone.com/articles/dependency-injection-in-net-core-console-applicati

https://code-maze.com/dependency-inversion-principle/

© www.soinside.com 2019 - 2024. All rights reserved.