Как сделать Windows Service из .Net Core 2.1 / 2.2 - PullRequest
0 голосов
/ 18 декабря 2018

Недавно у меня возникла необходимость преобразовать консольное приложение .Net Core 2.1 или 2.2 в службу Windows.

Поскольку у меня не было требований переносить этот процесс на Linux, я мог обойтись безНесколько платформенных решений, которые я видел в Stackoverflow, которые имели дело с любой комбинацией .Net Framework, .Net Standard и .Net Core.

Немного копания и большой помощи У меня было кое-что, что сработало:

1 Ответ

0 голосов
/ 18 декабря 2018

В этом посте я опишу шаги, необходимые для настройки процесса .Net Core 2.1 или 2.2 в качестве службы Windows.Поскольку у меня нет требований к Linux, я мог бы найти решение, которое было бы специфичным для Windows.Немного копания обнаружил некоторые сообщения от Стива Гордона (спасибо!), В частности, где он представляет пакет Microsoft.Extensions.Hosting и хостинг Windows (нажмите здесь для публикации и здесь для его образца github).Вот необходимые шаги:

  • Сначала создайте консольное приложение .Net Core.
  • Установите для языковой версии не менее 7,1 для поддержки асинхронной задачи для метода Main.(Доступ к языковой версии можно получить в настройках проекта-> Build-> Advanced-> Language Settings.
  • Добавить пакеты Microsoft.Extensions.Hosting и System.ServiceProcess.ServiceController.
  • Редактироватьпроект .csproj файл и включите в PropertyGroup: win7-x64
  • Убедитесь, что у вас есть в PropertyGroup Exe

Теперь перейдите к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();
            }
        }
    }
}

Этот код будет поддерживать интерактивную отладку и производственное выполнение и запускает пример класса 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
        }
    }
}

Последние два файла, необходимые для завершения проекта:

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

Чтобы установить, запустить или удалить службу, я использую утилиту 'sc':sc create AdvancedHost binPath = "C: \ temp \ AdvancedHost \ AdvancedHost.exe"где AdvancedHost - имя службы, а значение binPath - скомпилированный исполняемый файл.Как только служба создана, для запуска:sc start AdvancedHostПрекратить:sc stop AdvancedHostи, наконец, удалить (после остановки):sc удалить AdvancedHostЕсть много других функций, содержащихся в sc;просто введите «sc» в командной строке.Результаты sc можно увидеть в сервисной панели управления Windows.

...