В этом посте я опишу шаги, необходимые для настройки процесса .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.