C# 如何从.NET Core 2.1/2.2创建Windows服务
最近,我需要将.NET Core 2.1或2.2控制台应用程序转换为Windows服务C# 如何从.NET Core 2.1/2.2创建Windows服务,c#,.net,windows,.net-core,windows-services,C#,.net,Windows,.net Core,Windows Services,最近,我需要将.NET Core 2.1或2.2控制台应用程序转换为Windows服务 由于我不需要将这个过程移植到Linux,我可以省去我在堆栈溢出上看到的多个平台解决方案,这些解决方案处理.NET Framework的任何组合,.NET标准和.NET Core。在本文中,我将描述将.NET Core 2.1或2.2进程设置为Windows服务所需的步骤 因为我对Linux没有任何要求,所以我可以寻找一个特定于Windows的解决方案 经过一番挖掘,发现了Steve Gordon的一些帖子(谢
由于我不需要将这个过程移植到Linux,我可以省去我在堆栈溢出上看到的多个平台解决方案,这些解决方案处理.NET Framework的任何组合,.NET标准和.NET Core。在本文中,我将描述将.NET Core 2.1或2.2进程设置为Windows服务所需的步骤 因为我对Linux没有任何要求,所以我可以寻找一个特定于Windows的解决方案 经过一番挖掘,发现了Steve Gordon的一些帖子(谢谢!),特别是他展示了Microsoft.Extensions.Hosting包和Windows Hosting(点击查看帖子和他的GitHub示例) 以下是所需的步骤:
- 首先创建一个.NET核心控制台应用程序
- 将语言版本至少设置为7.1以支持主方法的异步任务。(从项目设置->构建->高级->语言设置访问语言版本
- 添加Microsoft.Extensions.Hosting和System.ServiceProcess.ServiceController包
- 编辑project.csproj文件并将其包含在PropertyGroup中:win7-x64
- 确保在PropertyGroup Exe中有
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();
}
}
}
}
完成项目所需的最后两个文件:
文件ServiceBaseLifetime.cs:
其中,AdvancedHost
是服务名称,binPath
的值是已编译的可执行文件
创建服务后,要启动:
sc start AdvancedHost
停止:
sc stop AdvancedHost
最后删除(一旦停止):
sc中包含了更多的功能;只需在命令行中单独键入“sc”
sc的结果可以在服务Windows控制面板中看到。您不再需要复制粘贴大量代码来执行此操作。 您只需安装该软件包 然后:
- 将
附加到HostBuilder。这还将配置应用程序以使用事件日志记录器UseWindowsService()
- 将项目中的SDK更改为Microsoft.NET.SDK.Worker(
) - 确保输出项目类型为EXE文件(
)EXE
- 将
附加到项目文件中win7-x64
dotnet publish
,sc create…
,等等
就这样。
这也适用于.NET Core 3.0/3.1。
阅读更多
下面显示了最小代码示例
.csproj文件:
Exe
win7-x64
netcoreapp2.1
文件Program.cs:
使用Microsoft.Extensions.DependencyInjection;
使用Microsoft.Extensions.Hosting;
命名空间NetcoreWindowsService
{
班级计划
{
静态void Main()
{
新主机生成器()
.ConfigureServices(服务=>services.AddHostedService())
.UseWindowsService()
.Build()
.Run();
}
}
}
文件MyService.cs:
使用Microsoft.Extensions.Hosting;
使用Microsoft.Extensions.Logging;
使用系统线程;
使用System.Threading.Tasks;
命名空间NetcoreWindowsService
{
内部类MyService:IHostedService
{
专用只读ILogger\u记录器;
公共MyService(ILogger logger)=>\u logger=logger;
公共任务StartSync(CancellationToken CancellationToken)
{
_logger.LogInformation(“服务已启动”);
返回Task.CompletedTask;
}
公共任务StopAsync(CancellationToken CancellationToken)
{
_logger.LogInformation(“服务已停止”);
返回Task.CompletedTask;
}
}
}
顶架一直是Windows服务的良好“框架”
它为所有windows服务共享的例程提供了良好的功能
特别是“安装”
基本原则是:
注意上面的nuget可以在NetStandard2.0下运行
现在在下面。您可以创建MyWindowsServiceExe.csproj..并将其编码为Exe/2.1或Exe/3.1(打开csproj时显示)。请注意,2.2不再是长期支持,我将避免将新代码写入2.2。(主题外,但该链接是)
然后是我提到的helper方法
MyWindowsServiceExe.exe安装
(现在检查控制面板下的windows服务以查看它是否已安装)(同时检查“如果崩溃,我应该怎么做”选项卡)
最后(另一个助手),您可以停止windows服务,您可以从命令行(windows服务外部)运行它。这是我最喜欢的调试
MyWindowsServiceExe.exe启动友好提示。2.2不是长期支持(LTS)。2.1和3.1是长期支持。我支持Mark McWhirter的答案,因为这是一个很好的答案。此答案作为TopShelf的备选答案和“供参考”。
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 create AdvancedHost binPath="C:\temp\AdvancedHost\AdvancedHost.exe"
sc start AdvancedHost
sc stop AdvancedHost
sc delete AdvancedHost
<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;
}
}
}
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();
}
);
}
dotnet publish -r win-x64 -c Release