Запуск службы c# с помощью sc

#c# #windows #service #worker

Вопрос:

Я пишу небольшой автономный сервис для Windows и macos, используя шаблон рабочего сервиса в visual studio c#.

Он использует ту же кодовую базу, отсюда и проверка в программе.cs

Я написал службу, и она работает в Windows, когда запускается из Visual studio.

Я опубликовал его, используя

 dotnet publish .WorkerServiceTest2 -c Release -r win-x64 -- self-contained true /p:PublishSingleFile=true /p:PublishedTrimmed=true
 

и попытался установить его с помощью

 runas /user:MYUSERNAME "sc.exe create WorkerServiceTest2 c:UsersMYYUSERNAMEDocumentsblablablaWorkerServiceTest2.exe"
 

Но он не отображается в списке услуг, и

 sc.exe start WorkerServiceTest2 
 

говорит, что эта служба не установлена.

Есть ли где-нибудь, где я могу увидеть, как sc.exe создать получилось ? Или, может быть, кто-то видит, что я делаю не так ?

Искренне Благодарю Вас

Моя сервисная программа.cs выглядит так

 using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Runtime.InteropServices;

namespace WorkerServiceTest2
{
    public class Program
    {
        public static void Main(string[] args)
        {
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)){
                Console.WriteLine("WinOS");
                CreateHostBuilderWin(args).Build().Run(); 
            } else
            {
                Console.WriteLine("MacOS");
                CreateHostBuilderMac(args).Build().Run();
            }

        }

        private static void configureServices(HostBuilderContext context, IServiceCollection services)
        {
            services.AddHostedService<Worker>();
        }

        public static IHostBuilder CreateHostBuilderWin(string[] args) =>
           Host.CreateDefaultBuilder(args)
           .UseWindowsService()
           .ConfigureServices((hostContext, services) =>
           {
               services.AddHostedService<Worker>();
           });

        public static IHostBuilder CreateHostBuilderMac(string[] args) =>
            Host.CreateDefaultBuilder(args)
            .ConfigureServices(configureServices);
    }

}

 

Мой рабочий.cs выглядит так

 using Microsoft.Extensions.Hosting;
using System.Threading;
using System.Threading.Tasks;
using WorkerServiceTest2.SocketService;

namespace WorkerServiceTest2

{
    public class Worker : BackgroundService
    {
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                //Her skal business logic være.
                SocketServer socketServer = new SocketServer();
                await socketServer.start();
            }
        }
    }
}

 

Комментарии:

1. Вы не возражаете использовать powershell вместо sc этого ?

2. Ну, в конце концов, он должен быть установлен на компьютере пользователя со сценарием, будет ли это проблемой ?

3. Вероятно, нет, но я ничего не знаю о вашей инфраструктуре. Но я могу дать вам powershell-скрипт, который я использую для установки всех своих служб.

4. Спасибо Вам за вашу помощь, я очень признателен ! Я бы хотел этого, просто чтобы посмотреть, есть ли там что-то, что работает на меня. ТАЙ!

Ответ №1:

Это скрипт, который вы можете использовать. Он проверит, установлена ли служба или нет. Если он уже существует, он удалит его и установит новый. Сохраните его как MyScript.ps1 (или по своему усмотрению) и запустите как:

 .MyScript.ps1 -serviceName name_of_service -serviceUsername some_username -servicePassword some_password -binaryPath "C:yourProgram.exe"
 

Скрипт:

 # Sample: howto run ps-script from power-shell:
#.Install-WindowsService_v3.ps1 -serviceName aTestservice -serviceUsername some_username -servicePassword some_password -binaryPath "C:yourProgram.exe"

param
     (
     [string]$serviceName,
     [string]$serviceUsername,
     [string]$servicePassword,
     [string]$binaryPath,
     [string]$startupType='Automatic',
     [string]$dependsOn
     )

$secpasswd = ConvertTo-SecureString  $servicePassword -AsPlainText -Force

Write-Output "########################################"
Write-Output "Starting installation of windows service."

Write-Output "[serviceName] = $serviceName"
Write-Output "[serviceUsername] = $serviceUsername" -verbose
Write-Output "[binaryPath] = $binaryPath"

#Check Parameters
if (!$binaryPath) { throw "[binaryPath] parameter missing" }
if ((Test-Path $binaryPath)-eq $false)
{
    Write-Output "Path doesn't exist: $binaryPath"
    Write-Output "Service will not be installed."
    throw [System.IO.FileNotFoundException] "$binaryPath doesn't exist."
}

# verify if the service already exists, and if yes remove it first
if (Get-Service $serviceName -ErrorAction SilentlyContinue)
{
    Stop-Service -Name $serviceName
    # using WMI to remove Windows service because PowerShell does not have CmdLet for this
    $serviceToRemove = Get-WmiObject -Class Win32_Service -Filter "name='$serviceName'"
    $serviceToRemove.delete()
    Write-Output "Service $serviceName was stopped and uninstalled."
}
else
{
    Write-Output "Service didn't exist on the server"
}


if ($startupType -eq "AutomaticDelayedStart" ) 
{ 
    $startupType = "Automatic"
    $enableDelayed = "true" 
}


Write-Output "Installing service"

# creating credentials which can be used to run my windows service
$mycreds = New-Object System.Management.Automation.PSCredential ($serviceUsername, $secpasswd)

# creating windows service using all provided parameters
New-Service -name $serviceName -binaryPathName $binaryPath -displayName $serviceName -startupType $startupType -credential $mycreds -DependsOn $dependsOn


# Set "automatic delayed" after service was installed, since it is not a valid argument when using "New-Service"
if ($enableDelayed -eq "true" ) 
{   
    $command = "sc.exe config $serviceName start= delayed-auto"
    $Output = Invoke-Expression -Command $Command -ErrorAction Stop
    if($LASTEXITCODE -ne 0){
       Write-Host "$Computer : Failed to set $serviceName to delayed start. 
        More details: $Output" -foregroundcolor red
       $failedcomputers  =$ComputerName
    } else {
       Write-Host "$Computer : Successfully changed $serviceName  
        to delayed start" -foregroundcolor green
       $successcomputers  =$ComputerName
    }
}

# verify if the service exists after installation
if (Get-Service $serviceName -ErrorAction SilentlyContinue)
{
    Write-Output "Installation complete."
}
else
{
    throw "Installation failed."
} 
Write-Output "########################################"
 

Кроме того, во всех моих приложениях я запускаю их так:

     static async Task Main(string[] args)
    {
        isService = !(Debugger.IsAttached || args.Contains("--console"));
        IWebHost host = CreateWebHostBuilder(args).Build();

        if (isService)
        {
            var hostService = new MyCustomWebService(host);
            ServiceBase.Run(hostService);
        }
        else
        {
            await host.RunAsync();
        }
    }


public class MyCustomWebService: WebHostService
{
    private ILogger<MyCustomWebService> logger;

    public MyCustomWebService(IWebHost host) : base(host)
    {
        var loggerFactory = host.Services.GetService<ILoggerFactory>();
        logger = loggerFactory.CreateLogger<MyCustomWebService>();
        logger.LogInformation("Starting...");
    }

    protected override void OnStopped()
    {
        logger.LogInformation("Will stop now.");
        base.OnStopped();
    }
}
 

Для этого требуется Microsoft.AspNetCore.Хостинг.Службы Windows

Далее рекомендуется прочитать: https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/windows-service?view=aspnetcore-5.0amp;tabs=visual-studio

https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.hosting.windowsservices?view=aspnetcore-5.0

Комментарии:

1. Большое Вам спасибо, я займусь этим прямо сейчас 🙂

2. Будет ли это запрашивать у пользователя учетные данные ? (Никогда раньше не видел сценарий powershell

3. Учетные данные передаются в качестве аргумента в сценарий powershell. Это учетные данные, от имени которых будет выполняться служба.

4. @KimSandberg — Если sc это единственный вариант. Попробуйте SC CREATE "name_of_service" binpath= "C:yourProgram.exe" , чтобы все в коде было одинаково.

5. да, спасибо за ваш комментарий. Я именно это и сделал. Для этого нужны повышенные права, но это не проблема. 🙂 спасибо за ваше время и помощь!