如何从.Net Core 3制作Windows服务

Mar*_*ter 4 .net c# windows service asp.net-core

最近,我需要将.Net Core 3.0控制台应用程序转换为Windows服务。

由于我不需要将此过程移植到Linux,因此我可以省去我在Stackoverflow上看到的涉及.Net Framework,.Net Standard和.Net Core任意组合的多平台解决方案。

编辑:Visual Studio 2019即将推出一个工作程序模板。由于它是预发布版本,因此会存在一些潜在的稳定性问题。我无法使用其中的一种参考,因此在模板稳定之前,我在下面提出的解决方案就足够了。(请参阅https://devblogs.microsoft.com/aspnet/net-core-workers-as-windows-services/

Mar*_*ter 7

  • 首先在Visual Studio 2019中创建.Net Core控制台应用程序。使用前,您需要安装.Net Core 3 SDK。还确保通过在“工具”->“选项”->“预览功能”中指定设置来引用.Net Core 3预览,“使用.NET Core SDK的预览”
  • 将语言版本至少设置为7.1,以支持Main方法的异步任务。(从项目设置->构建->高级->语言设置中访问语言版本)。.Net Core 3和Visual Studio 2019支持C#8.0
  • 添加Microsoft.Extensions.Hosting和System.ServiceProcess.ServiceController程序包。

现在转到Program.cs并复制以下内容:

using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace AdvancedHost
{
    internal class Program
    {
        private static async Task Main(string[] args)
        {
            var isService = !(Debugger.IsAttached || args.Contains("--console"));

            var builder = new HostBuilder()
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddHostedService<LoggingService>();
                });

            if (isService)
            {
                await builder.RunAsServiceAsync();
            }
            else
            {
                await builder.RunConsoleAsync();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

该代码将支持交互式调试和生产执行,并运行示例类LoggingService。

这是服务本身的骨架示例:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Concurrent;

namespace AdvancedHost
{
    public class LoggingService : IHostedService, IDisposable
    {

        public Task StartAsync(CancellationToken cancellationToken)
        {
            // Startup code

            return Task.CompletedTask;
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {           
            // Stop timers, services
            return Task.CompletedTask;
        }

        public void Dispose()
        {
            // dispose of non-managed resources
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

完成项目所需的最后两个文件:

ServiceBaseLifetime.cs:

using Microsoft.Extensions.Hosting;
using System;
using System.ServiceProcess;
using System.Threading;
using System.Threading.Tasks;

namespace AdvancedHost
{

    public class ServiceBaseLifetime : ServiceBase, IHostLifetime
    {
        private readonly TaskCompletionSource<object> _delayStart = new TaskCompletionSource<object>();

        public ServiceBaseLifetime(IApplicationLifetime applicationLifetime)
        {
            ApplicationLifetime = applicationLifetime ?? throw new ArgumentNullException(nameof(applicationLifetime));
        }

        private IApplicationLifetime ApplicationLifetime { get; }

        public Task WaitForStartAsync(CancellationToken cancellationToken)
        {
            cancellationToken.Register(() => _delayStart.TrySetCanceled());
            ApplicationLifetime.ApplicationStopping.Register(Stop);

            new Thread(Run).Start(); // Otherwise this would block and prevent IHost.StartAsync from finishing.
            return _delayStart.Task;
        }

        private void Run()
        {
            try
            {
                Run(this); // This blocks until the service is stopped.
                _delayStart.TrySetException(new InvalidOperationException("Stopped without starting"));
            }
            catch (Exception ex)
            {
                _delayStart.TrySetException(ex);
            }
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            Stop();
            return Task.CompletedTask;
        }

        // Called by base.Run when the service is ready to start.
        protected override void OnStart(string[] args)
        {
            _delayStart.TrySetResult(null);
            base.OnStart(args);
        }

        // Called by base.Stop. This may be called multiple times by service Stop, ApplicationStopping, and StopAsync.
        // That's OK because StopApplication uses a CancellationTokenSource and prevents any recursion.
        protected override void OnStop()
        {
            ApplicationLifetime.StopApplication();
            base.OnStop();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

ServiceBaseLifetimeHostExtensions.cs:

using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace AdvancedHost
{

    public static class ServiceBaseLifetimeHostExtensions
    {
        public static IHostBuilder UseServiceBaseLifetime(this IHostBuilder hostBuilder)
        {
            return hostBuilder.ConfigureServices((hostContext, services) => services.AddSingleton<IHostLifetime, ServiceBaseLifetime>());
        }

        public static Task RunAsServiceAsync(this IHostBuilder hostBuilder, CancellationToken cancellationToken = default)
        {
            return hostBuilder.UseServiceBaseLifetime().Build().RunAsync(cancellationToken);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

为了维护该服务,我使用了'sc'实用程序:创建:sc创建AdvancedHost binPath =“ C:\ temp \ AdvancedHost \ AdvancedHost.exe”,其中“ AdvancedHost”是服务名称,binPath的值是已编译的可执行文件。

状态:sc查询AdvancedHost

要启动:sc启动AdvancedHost

停止:sc停止AdvancedHost

删除(一旦停止):sc删除AdvancedHost

sc中包含更多功能。只需在命令行上单独输入“ sc”即可。sc的结果可以在服务Windows控制面板中看到。