Im writing a small selfcontained service for windows and macos using worker service template in c# visual studio.
Its using the same Codebase hence the check in the Program.cs
I've written the service, and it works on windows, when started from within visual studio.
I've published it using
dotnet publish .\WorkerServiceTest2\ -c Release -r win-x64 -- self-contained true /p:PublishSingleFile=true /p:PublishedTrimmed=true
and tried to install it using
runas /user:MYUSERNAME "sc.exe create WorkerServiceTest2 c:\Users\MYYUSERNAME\Documents\bla\bla\bla\WorkerServiceTest2.exe"
But it does not show up in the services list, and
sc.exe start WorkerServiceTest2
says this service is not installed.
Is there anywhere i can see how the sc.exe create worked out ? Or perhaps someone can see what I'm doing wrong ?
Sincerely Thankyou
My Service Program.cs looks like this
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);
}
}
My Worker.cs looks like this
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();
}
}
}
}
This is a script that you can use. It will check if the service is installed or not. If it already exists, it will uninstall it and install the new one. Save it as MyScript.ps1
(or your own preference) and run like:
.\MyScript.ps1 -serviceName name_of_service -serviceUsername some_username -servicePassword some_password -binaryPath "C:\yourProgram.exe"
Script:
# 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 "########################################"
Also, in all my application I start them up like so:
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();
}
}
It requires Microsoft.AspNetCore.Hosting.WindowsServices
Further recommended reading: https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/windows-service?view=aspnetcore-5.0&tabs=visual-studio