最近我需要将一个.NET Core 2.1或2.2控制台应用程序转换为Windows服务。
由于我不需要将此过程移植到Linux,因此我可以避免使用在Stack Overflow上看到的处理任何组合的.NET Framework、.NET Standard和.NET Core的多平台解决方案。
最近我需要将一个.NET Core 2.1或2.2控制台应用程序转换为Windows服务。
由于我不需要将此过程移植到Linux,因此我可以避免使用在Stack Overflow上看到的处理任何组合的.NET Framework、.NET Standard和.NET Core的多平台解决方案。
本文将介绍如何将.NET Core 2.1或2.2进程设置为Windows服务。
由于我没有Linux的需求,因此我可以寻找一种针对Windows的解决方案。
经过一些调查,我发现了Steve Gordon的一些文章(感谢他!),特别是他介绍Microsoft.Extensions.Hosting包和Windows托管的文章(点击这里查看文章,点击这里查看他的GitHub示例)。
以下是所需的步骤:
现在转到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
}
}
}
完成项目所需的最后两个文件:
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();
}
}
}
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 delete AdvancedHost
sc中包含许多其他特性;只需在命令行中单独输入'sc'即可查看。
使用服务Windows控制面板可以查看sc的结果。
UseWindowsService()
添加到HostBuilder。 这也配置了您的应用程序使用EventLog记录器。Microsoft.NET.Sdk.Worker
(<Project Sdk="Microsoft.NET.Sdk.Worker">
)。<OutputType>Exe</OutputType>
)<RuntimeIdentifier>win7-x64</RuntimeIdentifier>
附加到项目文件中。dotnet publish
、sc create ...
等命令。<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<OutputType>Exe</OutputType>
<RuntimeIdentifier>win7-x64</RuntimeIdentifier>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="3.1.3" />
</ItemGroup>
</Project>
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace NetcoreWindowsService
{
class Program
{
static void Main()
{
new HostBuilder()
.ConfigureServices(services => services.AddHostedService<MyService>())
.UseWindowsService()
.Build()
.Run();
}
}
}
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;
namespace NetcoreWindowsService
{
internal class MyService : IHostedService
{
private readonly ILogger<MyService> _logger;
public MyService(ILogger<MyService> logger) => _logger = logger;
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("The service has been started");
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("The service has been stopped");
return Task.CompletedTask;
}
}
}
Top shelf一直是Windows服务的一个很好的“框架”。
它为所有Windows服务共享的常规任务提供了良好的功能。
特别是“安装”功能。
基本原理如下:
https://www.nuget.org/packages/Topshelf/
请注意上面的NuGet可在NetStandard2.0下运行。
现在以下。 您可以创建MyWindowsServiceExe.csproj ..并将其编码为Exe/2.1或Exe/3.1(在打开csproj时显示)。 请注意,2.2不再是长期支持版本,我建议避免编写新代码到2.2。(离题了,但链接在这里 https://dotnet.microsoft.com/platform/support/policy/dotnet-core)
public class LoggingService : TopShelf.ServiceControl
{
private const string logFileFullName = @"C:\temp\servicelog.txt";
private void MyLogMe(string logMessage)
{
Directory.CreateDirectory(Path.GetDirectoryName(logFileFullName));
File.AppendAllText(logFileFullName, DateTime.UtcNow.ToLongTimeString() + " : " + logMessage + Environment.NewLine);
}
public bool Start(HostControl hostControl)
{
MyLogMe("Starting");
return true;
}
public bool Stop(HostControl hostControl)
{
MyLogMe("Stopping");
return true;
}
}
static void Main(string[] args)
{
HostFactory.Run(x =>
{
x.Service<LoggingService>();
x.EnableServiceRecovery(r => r.RestartService(TimeSpan.FromSeconds(333)));
x.SetServiceName("MyTestService");
x.StartAutomatically();
}
);
}
并将其构建/发布到Windows:
dotnet publish -r win-x64 -c Release
然后是我提到的辅助方法
MyWindowsServiceExe.exe install
(现在在控制面板下检查windows-services是否已安装)(也要检查“如果崩溃,我应该怎么做”选项卡)。
最后(另一个助手),您可以停止windows-service,您可以从命令行中运行它(在windows-service之外)。这是我用于调试的最爱。
MyWindowsServiceExe.exe start