How to make a Windows service from .Net Core 3 [closed]

浪子不回头ぞ 提交于 2021-01-27 04:16:53

问题


Recently I had a need to convert a .Net Core 3.0 console application into a Windows Service.

As I didn't have a requirement to port this process to Linux, I could dispense with the multiple platform solutions that I had seen on Stackoverflow that dealt with any combination of .Net Framework, .Net Standard and .Net Core.

EDIT: There is coming up for Visual Studio 2019 a worker template. As it is pre-release, there will be some potential stability issues. I could not get one of the references to work, so the solution I pose below should suffice until the template is stable. (see https://devblogs.microsoft.com/aspnet/net-core-workers-as-windows-services/)


回答1:


  • First create a .Net Core console application in Visual Studio 2019. You will need to install the .Net Core 3 SDK prior to use. Also insure you can reference the .Net Core 3 preview by specifying settings in Tools->Options->Preview Features, 'Use previews of the .NET Core SDK'
  • Set the language version to at least 7.1 to support async Task for the Main method. (Access the language version from the project settings->Build->Advanced->Language Settings). .Net Core 3 and Visual Studio 2019 support C# 8.0
  • Add the Microsoft.Extensions.Hosting and the System.ServiceProcess.ServiceController packages.

Now go to Program.cs and copy the following:

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();
            }
        }
    }
}

This code will support interactive debugging and production execution, and runs the example class LoggingService.

Here is a skeleton example of the service itself:

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
        }
    }
}

The final two files necessary to complete the project:

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();
        }
    }
}

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);
        }
    }
}

To maintain the service I use the 'sc' utility: To create: sc create AdvancedHost binPath="C:\temp\AdvancedHost\AdvancedHost.exe" where 'AdvancedHost' is the service name and the value for binPath is the compiled executable.

For status: sc query AdvancedHost

To start: sc start AdvancedHost

To stop: sc stop AdvancedHost

To delete (once stopped): sc delete AdvancedHost

There are many more features contained in sc; just type 'sc' alone on the command line. The results of sc can be seen in the services Windows control panel.



来源:https://stackoverflow.com/questions/56638847/how-to-make-a-windows-service-from-net-core-3

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!