简体   繁体   English

如何从 .NET Core 2.1/2.2 创建 Windows 服务

[英]How to make a Windows Service from .NET Core 2.1/2.2

Recently I had a need to convert a .NET Core 2.1 or 2.2 console application into a Windows Service.最近我需要将 .NET Core 2.1 或 2.2 控制台应用程序转换为 Windows 服务。

As I didn't have a requirement to port this process to Linux, I could dispense with the multiple platform solutions that I had seen on Stack Overflow that dealt with any combination of .NET Framework, .NET Standard and .NET Core.由于我不需要将此过程移植到 Linux,因此我可以放弃在 Stack Overflow 上看到的处理 .NET Framework、.NET Standard 和 .NET Core 的任意组合的多平台解决方案。

In this post I will describe the steps required to set up a .NET Core 2.1 or 2.2 process as a Windows Service.在本文中,我将描述将 .NET Core 2.1 或 2.2 进程设置为 Windows 服务所需的步骤。

As I have no requirement for Linux, I could look for a solution that was Windows-specific.因为我对 Linux 没有要求,所以我可以寻找特定于 Windows 的解决方案。

A bit of digging turned up some posts from Steve Gordon (thanks,).一些挖掘发现了史蒂夫戈登的一些帖子(谢谢,)。 in particular where he presents the Microsoft.Extensions.Hosting package and Windows hosting (click here for post and here for his GitHub sample).特别是他展示了 Microsoft.Extensions.Hosting 包和 Windows 托管(单击此处查看帖子,单击此处查看他的 GitHub 示例)。

Here are the steps required:以下是所需的步骤:

  • First create a .NET Core console application.首先创建一个 .NET Core 控制台应用程序。
  • Set the language version to at least 7.1 to support async Task for the Main method.将语言版本设置为至少 7.1 以支持 Main 方法的异步任务。 (Access the language version from the project settings->Build->Advanced->Language Settings. (从项目设置->构建->高级->语言设置中访问语言版本。
  • Add the Microsoft.Extensions.Hosting and the System.ServiceProcess.ServiceController packages.添加 Microsoft.Extensions.Hosting 和 System.ServiceProcess.ServiceController 包。
  • Edit the project.csproj file and include in the PropertyGroup: <RuntimeIdentifier>win7-x64</RuntimeIdentifier>编辑 project.csproj 文件并在 PropertyGroup 中包含:<RuntimeIdentifier>win7-x64</RuntimeIdentifier>
  • Ensure you have in the PropertyGroup <OutputType>Exe</OutputType>确保您在 PropertyGroup <OutputType>Exe</OutputType>

Now go to Program.cs and copy the following:现在转到 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();
            }
        }
    }
}

This code will support interactive debugging and production execution, and runs the example class LoggingService.此代码将支持交互式调试和生产执行,并运行示例类 LoggingService。

Here is a skeleton example of the service itself:这是服务本身的骨架示例:

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
        }
    }
}

The final two files necessary to complete the project:完成项目所需的最后两个文件:

File ServiceBaseLifetime.cs :文件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();
        }
    }
}

File ServiceBaseLifetimeHostExtensions.cs :文件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);
        }
    }
}

In order to install, run or delete the service I use the 'sc' utility:为了安装、运行或删除我使用“sc”实用程序的服务:

sc create AdvancedHost binPath="C:\temp\AdvancedHost\AdvancedHost.exe"

where AdvancedHost is the service name and the value for binPath is the compiled executable.其中AdvancedHost是服务名称, binPath的值是已编译的可执行文件。

Once the service is created, to start:创建服务后,开始:

sc start AdvancedHost

To stop:停止:

sc stop AdvancedHost

And finally to delete (once stopped):最后删除(一旦停止):

sc delete AdvancedHost

There are many more features contained in sc; sc 中包含更多功能; just type 'sc' alone on the command line.只需在命令行中单独输入“sc”。

The results of sc can be seen in the services Windows control panel. sc 的结果可以在服务 Windows 控制面板中看到。

You no longer need to copy-paste a lot of code to do it.您不再需要复制粘贴大量代码来完成它。 All you need is to install the package Microsoft.Extensions.Hosting.WindowsServices您只需要安装软件包Microsoft.Extensions.Hosting.WindowsServices

Then:然后:

  • Append UseWindowsService() to the HostBuilder.将 UseWindowsService UseWindowsService()附加到 HostBuilder。 This will also configure your application to use the EventLog logger.这还将配置您的应用程序以使用 EventLog 记录器。
  • Change the SDK in your project to Microsoft.NET.Sdk.Worker ( <Project Sdk="Microsoft.NET.Sdk.Worker"> ).将项目中的 SDK 更改为Microsoft.NET.Sdk.Worker ( <Project Sdk="Microsoft.NET.Sdk.Worker"> )。
  • Make sure that the output project type is EXE file ( <OutputType>Exe</OutputType> )确保输出项目类型是 EXE 文件 ( <OutputType>Exe</OutputType> )
  • Append <RuntimeIdentifier>win7-x64</RuntimeIdentifier> to the project file.<RuntimeIdentifier>win7-x64</RuntimeIdentifier>附加到项目文件。

Debug your service like a regular console application, and then run dotnet publish , sc create... , etc.像常规控制台应用程序一样调试您的服务,然后运行dotnet publishsc create...等。

That's it.而已。 This also works for .NET Core 3.0/3.1.这也适用于 .NET Core 3.0/3.1。 Read more here . 在这里阅读更多。

The minimal code example is shown below.最小代码示例如下所示。

.csproj file: .csproj 文件:

<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>

File Program.cs :文件Program.cs

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();
        }
    }
}

File MyService.cs :文件MyService.cs

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 has been a good "framework" for Windows Services. Top shelf 一直是 Windows 服务的良好“框架”。

It provides good functionality for the routine stuff that all windows services share.它为所有 Windows 服务共享的常规内容提供了良好的功能。

Especially "install".特别是“安装”。

The basics are:基础知识是:

https://www.nuget.org/packages/Topshelf/ https://www.nuget.org/packages/Topshelf/

Note the nuget above can be run under NetStandard2.0.注意上面的nuget可以在NetStandard2.0下运行。

Now below.现在在下面。 You can create MyWindowsServiceExe.csproj.. and code it as an Exe/2.1 or Exe/3.1 (shown when you open up the csproj).您可以创建 MyWindowsServiceExe.csproj.. 并将其编码为 Exe/2.1 或 Exe/3.1(打开 csproj 时显示)。 Note that 2.2 is not Long Term Support anymore and I would avoid writing new code to 2.2.请注意,2.2 不再是长期支持,我会避免为 2.2 编写新代码。 (off topic, but that link is https://dotnet.microsoft.com/platform/support/policy/dotnet-core ) (题外话,但该链接是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();
     }
);
}

and build/publish it to windows:并将其构建/发布到 Windows:

dotnet publish -r win-x64 -c Release

and then the helper methods I mentioned然后是我提到的辅助方法

MyWindowsServiceExe.exe install安装MyWindowsServiceExe.exe

(now check windows-services under control panel to see it installed) (check "if crash, what should I do" tab as well). (现在检查控制面板下的 windows-services 以查看它是否已安装)(同时检查“如果崩溃,我应该做什么”选项卡)。

and finally (another helper), you can STOP the the windows-service, you can run it from the command line (outside of windows-services) This is my favorite for debugging.最后(另一个助手),你可以停止 windows 服务,你可以从命令行运行它(在 windows 服务之外)这是我最喜欢的调试方式。

MyWindowsServiceExe.exe start MyWindowsServiceExe.exe 启动

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 .net core 2.2 Windows服务完全启动后,如何配置托管服务才能开始? - How to configure hosted service to begin after .net core 2.2 windows service fully started? 在2.1中使用.Net Core 2.2类 - Using .Net Core 2.2 classes in 2.1 通过topshelf(.net core 2.2)将hangfire用作Windows服务 - use hangfire as windows service by topshelf (.net core 2.2) 如何使用Topshelf将asp.net Core 2.1 MVC应用程序部署为Windows服务 - How to deploy asp.net core 2.1 MVC application as windows service using topshelf 从.net Core 2.1中的单例服务注入范围服务 - Injecting scoped service from singleton service in .net core 2.1 如何在Raspberry PI 3的Windows 10 IoT核心版中从ASP.NET Core 2.1调用UWP函数 - How to call UWP function from ASP.NET Core 2.1 in Windows 10 IoT Core for Raspberry PI 3 如何使函数从asp.net core 2.2上的路径下载文件? - How to make function make download file from path on asp.net core 2.2? 从2.1-&gt; 2.2的ASP.NET Core迁移-Azure部署中的依赖关系问题 - ASP.NET Core Migration from 2.1 -> 2.2 Issue with dependencies in Azure deployment .NET Core 2.2:如何在 Azure 应用服务中从 Azure AD 自动填充用户列表? - .NET Core 2.2 : How to auto-populate list of users from Azure AD in Azure app service.? Service Fabric .NET Core 2.1服务通信 - Service Fabric .NET Core 2.1 service communications
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM