Search code examples
.netwindows-servicesbackground-service.net-core-3.0

Cannot start windows service when using Microsoft.Extensions.Hosting.WindowsServices


I tried to follow the recommendations here for creating a Windows service using Microsoft.Extensions.Hosting.WindowsServices. So far so good, everything works fine, my background service's ExecuteAsync gets called and the log says that everything is OK. Starting the application as a console application works fine, too, I can start it, do whatever I need to do, then stop it.

However, I try then to install the Windows service using:

sc create myservice binPath= "\"<path-to-the-exe-file>\" service" start= auto DisplayName= "My Service"

I get [SC] CreateService SUCCESS. But when I try to start the service manually, it tells me that it did not respond in timely manner. Again, the log is fine, no errors. The Event Viewer tells me nothing more about what it can go wrong and I don't have any clue what can I do next to find the cause of the problem.

Here is an approximate code I used to configure the host:

var containerBuilder = new ContainerBuilder();
IContainer container = null; 

var hostBuilder = Host.CreateDefaultBuilder(appArgs);
hostBuilder
    .UseServiceProviderFactory(new CustomAutofacServiceProviderFactory(() => container))
    .ConfigureServices(services =>
    {
        services.AddHostedService<BackgroundWorker>();
        containerBuilder.Populate(services);
        container = containerBuilder.Build();
    })
    .UseWindowsService();

And this is the class I use for the background service.

public class BackgroundWorker : BackgroundService
{
    private readonly IAppContext appContext;
    private CancellationTokenRegistration stopRegistration;

    public BackgroundWorker(ILogger<BackgroundWorker> logger, IAppContext appContext)
    {
        this.Logger = logger;
        this.appContext = appContext;
    }

    public ILogger<BackgroundWorker> Logger { get; }

    protected override Task ExecuteAsync(CancellationToken stoppingToken)
    {
        this.Logger.Info("Background worker started.");

        this.stopRegistration = stoppingToken.Register(() =>
        {
            this.Logger.Info("Background worker stopping...");
            this.stopRegistration.Dispose();
            this.Logger.Info("Background worker stopped.");
        });

        return Task.CompletedTask;
    }
}

Solution

  • I found the answer just now, after a couple of hours of struggling.

    I am using Autofac as a Dependency Injection container and in HostBuilder.ConfigureServices() I was building the Autofac container.

    AFTER THAT I called .UseWindowsService(), which was too late, as Autofac was already done with building the container.

    So, the answer is, use .UseWindowsService() BEFORE the container is built, otherwise it will have no effect on the composed services.

    Here is the changed code that is working:

    var containerBuilder = new ContainerBuilder();
    IContainer container = null; 
    
    var hostBuilder = Host.CreateDefaultBuilder(appArgs);
    hostBuilder
        .UseServiceProviderFactory(new CustomAutofacServiceProviderFactory(() => container))
        .ConfigureServices(services =>
        {
            services.AddHostedService<BackgroundWorker>();
        })
        .UseWindowsService() //<-- Done BEFORE building container
        .ConfigureServices(services =>
        {
            containerBuilder.Populate(services);
            container = containerBuilder.Build();
        })