C# Windows services can be a pain to develop because they are awkward to test, debug and run locally. However a few tweaks to the default VS template make them far more manageable.
Why use a windows service in the first place?
At first glance a Windows Service looks like old tech in an age of http everywhere. However they are a reliable proven platform that is easy to deploy, easy to remotely monitor, and it is actually trivial to embed a web-server inside one and ditch IIS entirely. This makes them a good fit for running background tasks as a companion to your website, or for services where IIS is overkill or unwanted dependency. Windows services can run on any windows box without dependencies like IIS, auto-start on boot and run as a given user if needed. Popular .net dev web apps like Teamcity and Octopus Deploy run as Windows Services - they are a proven platform for web apps too.
The default development experience is painful
My biggest complaint is that the development experience is poor compared to traditional web or console apps. The default template is basically a console app with a special Service.cs Designer class with an OnStart/OnStop method for you to place your code. When you try and run the executable or F5 debug, you just get the Windows Service Start Failure error:
Cannot start service from the command line or a debugger. A Windows Service must first be installed using InstallUtil.exe and then started with the Server Explorer, Windows Services Administrative Tool or the NET START command.
Not very helpful. Microsoft’s guide to debugging services is an eleven step process to get a not quite normal attached debug experience. And add to that InstallUtil.exe is probably not available on your deployment destination so you may need to bundle it with your release (which is why I always install with sc.exe instead as it is available everywhere).
What if it was just a console app?
Topshelf?
There is a great library for simplifying Windows Service development called Topshelf. It has a set of features for simplifying installation and by default supports running the service on the console, giving us back a decent development experience however I usually find it has more features than I need. What i want can be achieved with a few lines of code.
Just tweak the template for console support
Change to the default template to get F5 debugging and run as a console support:
Step 1: Add a public RunAsConsole
method to your service class.
public void RunAsConsole(string[] args)
{
OnStart(args);
Console.WriteLine("Press any key to exit...");
Console.ReadLine();
OnStop();
}
Step 2: Opt out of the ServiceBase when running as a console
Change Program.cs
main method so it opts out of full service mode when running in interactive mode (i.e. as a console), while falling back to normal service behaviour when run by the Service Controller:
static class Program
{
static void Main(string[] args)
{
MyService service = new MyService();
if (Environment.UserInteractive)
{
service.RunAsConsole(args);
}
else
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[] { service };
ServiceBase.Run(ServicesToRun);
}
}
}
Step 3: Change the output type
The default Output Type in the Windows Service template is Windows Application. Change this to Console Application (in the projects Properties). This will make no difference to your service, but now you can actually debug your application
Console for development, Service for production
Run your service. You now have F5 debug support by default and if you run your executable it will behave just like a console application. But if you install it as a service, it will work just as expected. Perhaps the template should have been like this to start with?