Creating a Windows Service

Creating a windows service in .net is simple. Dead simple. Creating one that's pleasant to work with is only slightly more complicated. We're talking a couple of dozen LOC in total, it's that simple.

This is why I've never been able to understand the love for Topshelf. It adds another dependency to manage yet saves very little, if any, code.

So I figured I'd create a post to show just how easy it is.

Step 1 - Create the App

First thing to do is to create the logic the service needs. This town crier example is straight from Topshelf's show me the code section:

public class TownCrier {
    readonly Timer _timer;
    public TownCrier() {
        _timer = new Timer(1000) { AutoReset = true };
        _timer.Elapsed += (sender, eventArgs) => Console.WriteLine("It is {0} and all is well", DateTime.Now);
    }
    public void Start() { _timer.Start(); }
    public void Stop() { _timer.Stop(); }
}

Then we need to run it, this can be done withe a standard console app:

class Program {
    static void Main(string[] args) {
        var crier = new TownCrier();
        crier.Start();
        System.Console.WriteLine("press any key to quit");
        System.Console.Read();
        crier.Stop();
    }
}

Nothing amazing yet.

Turn it into a Service

Turning this into a service requires some extra communication with the OS. In .net this is handled by the ServiceBase class (you will need to add a reference to System.ServiceProcess).

To create our own service, we just create a sub class and add our own logic:

public class ServiceHost : ServiceBase {
    private readonly TownCrier TownCrier;

    public ServiceHost(TownCrier crier) {
        TownCrier = crier;
    }

    protected override void OnStart(string[] args) {
        TownCrier.Start();
    }

    protected override void OnStop() {
        TownCrier.Stop();
    }
}

Then modify our main method to run the host:

static void Main(string[] args) {
    var crier = new TownCrier();
    System.ServiceProcess.ServiceBase.Run(new ServiceHost(crier));
}

If you run this, you'll see an error dialog displayed. This is because services cannot be run from a normal application.

To handle this, we need to add a check to set what environment we're running in. This can be done with the System.Environment.UserInteractive property. If this is true, it means we are running the app as a user, false means it is running as a service.

The main method then becomes a combination of the previous versions:

static void Main(string[] args) {
    var crier = new TownCrier();
    if (System.Environment.UserInteractive) {
        crier.Start();
        System.Console.WriteLine("press any key to quit");
        System.Console.Read();
        crier.Stop();
    } else {
        System.ServiceProcess.ServiceBase.Run(new ServiceHost(crier));
    }
}

This way, when you run the app normally, or debug it, it behaves as a normal console app, but it can also now be run as a service. Let's create the service.

Create a Service

The sc tool is the one to use when dealing with windows services. It can be used to list create, start, stop and delete them.

To create and start the service, open up a command prompt (with admin privileges) and enter these commands:

sc create TownCrier binpath= c:\path\to\bin\Debug\TownCrier.exe
sc start TownCrier

You can verify it's running with this command:

sc query TownCrier

And that's all there is to it.