C# 如何从.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的一些帖子(谢

最近,我需要将.NET Core 2.1或2.2控制台应用程序转换为Windows服务


由于我不需要将这个过程移植到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中有
现在转到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();
            }
        }
    }
}
完成项目所需的最后两个文件:

文件ServiceBaseLifetime.cs: 其中,
AdvancedHost
是服务名称,
binPath
的值是已编译的可执行文件

创建服务后,要启动:

sc start AdvancedHost
停止:

sc stop AdvancedHost
最后删除(一旦停止):

sc中包含了更多的功能;只需在命令行中单独键入“sc”


sc的结果可以在服务Windows控制面板中看到。

您不再需要复制粘贴大量代码来执行此操作。 您只需安装该软件包

然后:

  • UseWindowsService()
    附加到HostBuilder。这还将配置应用程序以使用事件日志记录器
  • 将项目中的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