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

标签 c# .net windows .net-core windows-services

最近我需要将 .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/

相关文章:

C# 斐波那契数列复制

c# - 在 C# 的 linq 中选择大小写

c# - WPF:获取堆栈面板中触摸的项目索引

windows - 从 Linux 在远程 Windows 上运行 bat 文件

windows - WMI Win32_PerfRawData_PerfOS_PagingFile PercentUsage 图中的可疑值

c# - 透明的富文本框

c# - 从服务访问 WCF 客户端凭据

c# - 将字符串拆分为两部分

c# - VB 编译器不隐式转换为对象?

windows - 用于 VC++ 的 DSP SDK