I have a background with Linux for running services, first time working on Microsoft environment. Code could be improved in many areas I'm sure, first step is getting a running service. I've referenced many posts and documentation, but I'm not yet able to fully understand how Microsoft services and dotnet interact as compared to a linux environment with apache running a django application for example.
I'm using Visual Basic for Mac as development, dotnet 5.0 C# environment to build a service that listens to a directory for new file creation and then runs a script with the file information. I deploy to a Windows 10 x-64 with dotnet 5.0 environment. I can compile an .exe and run on the target machine and it works fine. I then build the service with sc.exe, and whether I run from the Services Manager GUI or from Powershell, the service will always time out. I am trying to figure out how to get the service to continue running. The Service will write to my log files, and it will even process the file if a new file as added to the target directory, but then it will always proceed to time out with the following error:
Error 1053: the service did not respond to the start or control request in a timely fashion
My steps and code below:
Create Worker Service with Visual Studio for Mac.
My Program.cs
was built from a template and added a few other methods that are empty now, but I thought would be useful.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace MyService
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
//config.AddJsonFile("custom_settings.json");
})
.ConfigureLogging(logging =>
{
//builder.AddFile("logs/app-{Date}.json"), isJson: true);
})
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker>();
});
}
}
Next is Worker.cs
:
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using HttpClientSample;
namespace MyService
{
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
public override async Task StartAsync(CancellationToken cancellationToken)
{
string path = @"C:\log.txt";
string startText = "Start" Environment.NewLine;
File.AppendAllText(path, startText);
await ExecuteAsync(cancellationToken);
}
public override async Task StopAsync(CancellationToken cancellationToken)
{
string path = @"C:\log.txt";
string stopText = "Stop" Environment.NewLine;
File.AppendAllText(path, stopText);
await base.StopAsync(cancellationToken);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
string path = @"C:\log.txt";
string writeText = "Execute Async" Environment.NewLine;
File.AppendAllText(path, writeText);
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = @"C:\files\";
watcher.NotifyFilter = NotifyFilters.FileName;
watcher.Created = OnCreated;
watcher.Filter = "*.*";
watcher.EnableRaisingEvents = true;
File.AppendAllText(path, "watcher engaged.");
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("worker running at: {time}", DateTimeOffset.Now);
try
{
await Task.Delay(1000, stoppingToken);
}
catch (OperationCanceledException)
{
break;
}
}
}
private static void OnCreated(object sender, FileSystemEventArgs e)
{
string path = @"C:\log.txt";
string writeText = $"e: {e}";
File.AppendAllText(path, writeText);
MyProgram myProgram = new MyProgram(e.FullPath);
}
}
}
I then process in MyProgram and everything works fine.
For the service, I first compile to correct runtime with the following command: dotnet publish -c Release -r win10-x64
. Then I FTP the publish folder over to the Windows 10 machine, run the .exe as the admin profile and it works fine, then create a service from the file via the following: sc.exe create myService binPath= "C:\path\to\executable.exe"
. I then proceed to run this service from the Services Manager or from Powershell with sc.exe start myService
. After approximately 60 seconds I receive the 1053 error.
I don't understand what's required to keep the service running. Steps I've tried below:
- I've ensured I'm pointing to the exe when building the service.
- I've already increased the timeout via registry as suggested in several sites: here.
- I've checked in the Event Viewer for dotnet or services manager issues and wasn't able to find the cause of the issue.
- I've tried the local system account as well as admin account to run the service and it still times out.
- I've seen InstallUtil as a suggested alternative to sc.exe, but I haven't tried it. I'm not sure how much a difference the tool makes?
- Would using the WindowsService vs BackgroundService make the difference when trying to run this via Services Manager?
- Is there some signal or return value that the Program needs to implement in order to let the Service know to continue running? There's already a Task.delay() implemented and return values for the StartTask and ExecuteTask methods.
Any suggestions on troubleshooting, solving the problem, or improving my post would all be appreciated. Thank you!
CodePudding user response:
A service process must call the right dll function to indicate that it is starting, and to receive pause / stop events.
In .net there's an implementation of these dll calls built into System.ServiceProcess.ServiceBase
There's an implementation of a service built into asp.net, which I believe pre-dates the introduction of the generic host. Just calling IWebHost.RunAsService()
would be enough to glue the windows service start / stop events into the web host start / stop lifetime.
I believe all you need to do is call .UseWindowsService()
when configuring your host builder. And this should work for either a console program, or a web host.