最近我需要将 .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 主机的地方(点击 here 查看帖子,点击 here 查看他的 GitHub 示例) .
以下是所需的步骤:
- 首先创建一个 .NET Core 控制台应用程序。
- 将语言版本至少设置为 7.1 以支持 Main 方法的异步任务。 (从项目设置->构建->高级->语言设置中访问语言版本。
- 添加 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 delete AdvancedHost
sc 中包含更多功能;只需在命令行中单独键入“sc”即可。
sc 的结果可以在服务 Windows 控制面板中看到。
关于c# - 如何从 .NET Core 2.1/2.2 创建 Windows 服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53837763/