1

我在 Windows 上的 Visual Studio 2019 中开发 .NET Core 控制台应用程序(准备在装有 Windows 10 的服务器上作为 Windows 服务运行),我的持续开发工具在 CentOS 8 (GoCD) 上。

当我在 Windows 上发布该应用程序并作为服务运行时,一切都很好。当我尝试在 Linux 上发布并在 Windows 10 上运行该构建的应用程序时,我得到:

未处理的异常。System.PlatformNotSupportedException:ServiceController 允许操作和访问 Windows 服务,它不适用于其他操作系统。在 System.ServiceProcess.ServiceBase..ctor() 在 appIV.Scheduler.ServiceBaseLifetime..ctor(IApplicationLifetime applicationLifetime) 在 /opt/gocd-agent-1/go-agent-19.12.0/pipelines/app-SCHEDULER-PRODUCTION/ app-SCHEDULER-PRODUCTION/appIV/appIV.Scheduler/Service/ServiceBaseLifetime.cs:line 15

CentOS 8 上的环境:

dotnet --list-runtimes
Microsoft.AspNetCore.App 3.1.3 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 3.1.3 [/usr/share/dotnet/shared/Microsoft.NETCore.App]

dotnet --list-sdks
3.1.201 [/usr/share/dotnet/sdk]

我使用以下命令发布应用程序

dotnet publish -o /tmp/app/scheduler/bin/Release/netcoreapp3.1/win-x64/publish/ -c Release -r win-x64 --self-contained true

我在想-r win-x64参数足以在具有 Windows 目标的 Linux 上进行 dotnet 发布,但显然我错了。只要应用程序作为常规控制台应用程序运行(我可以使用 --console 参数将该服务作为控制台应用程序运行),它就可以运行。但由于某种原因,我不明白在 Linux 上发布的应用程序不能在 Windows 上作为服务运行。

类管理作为服务运行,发生错误的地方:

using Microsoft.Extensions.Hosting;
using NLog;
using System;
using System.ServiceProcess;
using System.Threading;
using System.Threading.Tasks;

namespace App.Scheduler
{
    public class ServiceBaseLifetime : ServiceBase, IHostLifetime
{
    private readonly TaskCompletionSource<object> _delayStart = new TaskCompletionSource<object>();
    private NLog.Logger _logger = LogManager.GetCurrentClassLogger();

    public ServiceBaseLifetime(IApplicationLifetime applicationLifetime)
    {
        ApplicationLifetime = applicationLifetime ?? throw new ArgumentNullException(nameof(applicationLifetime));
    }

    private IApplicationLifetime ApplicationLifetime { get; }

    public Task WaitForStartAsync(CancellationToken cancellationToken)
    {
        _logger.Debug("WaitForStartAsync");
        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
        {
            _logger.Debug("Run");
            Run(this); // This blocks until the service is stopped.
            _delayStart.TrySetException(new InvalidOperationException("Stopped without starting"));
        }
        catch (Exception ex)
        {
            _logger.Error(ex.Message);
            _delayStart.TrySetException(ex);
        }
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.Debug("StopAsync");
        Stop();
        return Task.CompletedTask;
    }

    // Called by base.Run when the service is ready to start.
    protected override void OnStart(string[] args)
    {
        _logger.Debug("OnStart");
        _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()
    {
        _logger.Debug("OnStop");
        ApplicationLifetime.StopApplication();
        base.OnStop();
    }
}

}

4

0 回答 0