Creating Windows Services In .NET Core – Part 2 – The “Topshelf” Way

This article is part of a series on creating Windows Services in .NET Core.

Part 1 – The “Microsoft” Way
Part 2 – The “Topshelf” Way
Part 3 – The “.NET Core Worker” Way


In our previous piece on creating Windows Services in .NET Core, we talked about how to do things the “Microsoft” way. What we found was that while it was simple to get up and running, it was much harder to debug our service. Infact I would say borderline impossible.

And that’s where Topshelf comes in. Topshelf is a .NET Standard library that takes a tonne of the hassle out of creating Windows Services in both .NET Framework and .NET Core. But rather than recite the sales pitch to you, let’s jump right in!

Setup

Similar to our “Microsoft” method, there is no Windows Service or Topshelf “Visual Studio Template”. We instead just create a regular old .NET Core console application.

Then from our Package Manager Console, we run the following to install the Topshelf libraries.

Install-Package Topshelf

The Code

We essentially want to recreate our previous code sample to work with Topshelf. Here’s how we do that :

public class LoggingService : ServiceControl
{
    private const string _logFileLocation = @"C:\temp\servicelog.txt";

    private void Log(string logMessage)
    {
        Directory.CreateDirectory(Path.GetDirectoryName(_logFileLocation));
        File.AppendAllText(_logFileLocation, DateTime.UtcNow.ToString() + " : " + logMessage + Environment.NewLine);
    }

    public bool Start(HostControl hostControl)
    {
        Log("Starting");
        return true;
    }

    public bool Stop(HostControl hostControl)
    {
        Log("Stopping");
        return true;
    }
}

All rather simple.

We inherit from the “ServiceControl” class (Which isn’t actually needed but it just provides a good base for us to work off). We have to implement the two methods to start and stop, and we just log those methods as we did before.

In our Main method of program.cs, it’s actually really easy. We can just use the HostFactory.Run method to kick off our service with minimal effort :

static void Main(string[] args)
{
    HostFactory.Run(x => x.Service<LoggingService>());
}

Crazy simple. But that’s not the only thing HostFactory can do, for example I may want to say that when my service crashes, I want to restart the service after 10 seconds, give it a nicer name, and set it to start automatically.

static void Main(string[] args)
{
    HostFactory.Run(x =>
        {
            x.Service<LoggingService>();
            x.EnableServiceRecovery(r => r.RestartService(TimeSpan.FromSeconds(10)));
            x.SetServiceName("TestService");
            x.StartAutomatically();
         }
    );
}

I could go on and on but just take a look through the Topshelf documentation for some configuration options. Essentially anything you would normally have to painfully do through the windows command line, you can set in code : https://topshelf.readthedocs.io/en/latest/configuration/config_api.html

Deploying Our Service

Same as before, we need to publish our app specifically for a Windows environment. From a command prompt, in your project directory, run the following :

dotnet publish -r win-x64 -c Release

Now we can check out the output directory at bin\Release\netcoreappX.X\win-x64\publish and we should find that we have a nice little exe waiting for us to be installed.

Previously we would use typical SC windows commands to install our service, but Topshelf utilizes it’s own command line parameters for installing as a service. Almost all configuration that you can do in code you can also do from the command line (Like setting recovery options, service name/description etc). You can check out the full documentation here :

For us, we are just going to do a bog standard simple install. So in our output directory, I’m going to run the following from the command line : http://docs.topshelf-project.com/en/latest/overview/commandline.html

WindowsServiceExample.exe install

Where WindowsServiceExample.exe is my project output. All going well my service should be installed! I often find that even when setting the service to startup automatically, it doesn’t always happen. We can actually start the service from the command line after installation by running :

WindowsServiceExample.exe start

In deployment scenarios I often find I have to install the service, wait 10 seconds, then attempt to start it using the above and we are away laughing.

Debugging Our Service

So when doing things the “Microsoft” way, we ran into issues around debugging. Mostly that we had to either use command line flags, #IF DEBUG directives, or config values to first work out if we are even running inside a service or not. And then find hacky ways to try and emulate the service from a console app.

Well, that’s what Topshelf is for!

If we have our code open in Visual Studio and we just start debugging (e.g. Press F5), it actually emulates starting the service in a console window. We should be met with a message saying :

The TestService service is now running, press Control+C to exit.

This does exactly what it says on the tin. It’s started our “service” and runs it in the background as if it was running as a Windows Service. We can set breakpoints as per normal and it will basically follow the same flow as if it was installed normally.

We can hit Ctrl+C and it will close our application, but not before running our “Stop” method of our service, allowing us to also debug our shutdown procedure if required. Compared to debug directives and config flags, this is a hell of a lot easier!

There is just one single gotcha to look out for. if you get a message similar to the following :

The TestService service is running and must be stopped before running via the console

This means that the service you are trying to debug in Visual Studio is actually installed and running as a Windows Service on the same PC. If you stop the Windows Service from running (You don’t have to uninstall, just stop it), then you can debug as normal.

What’s Next

A helpful reader pointed out in the previous article that .NET Core actually has a completely different way of running Windows Services. It essentially utilizes the “Hosted Services” model that has been introduced into ASP.NET Core and allows them to run as Windows Services which is pretty nifty!

ENJOY THIS POST?
Join over 3.000 subscribers who are receiving our weekly post digest, a roundup of this weeks blog posts.
We hate spam. Your email address will not be sold or shared with anyone else.

3 comments

  1. The title “Creating Windows Services In .NET Core ‘Topshelf Way’ is fake!
    When runinng, the service shows the version 4.xxx from .net framework…
    topshelf is a cool tool but depends on old framework…

    1. Hi Fred,

      Do you have a gist/repo so I can check out your issue? Topshelf is .NET Standard so itself shouldn’t run under any framework.

  2. Hi Fred,

    Topshef supports .Net Standard 2.0 and is good with .net core. I created a Windows Service on .Net Core 3.1 and referred to TopShelf v4.2.1.215
    When debugging the service it incorrectly says .Net Framework but is showing the v3.1.0 which is .net core 3.1 for my project.

    service showing: ‘Topshelf v4.2.1.215, .NET Framework v3.1.0’
    Thanks,

Leave a Reply

Your email address will not be published. Required fields are marked *