About six months ago I wrote a tiny bit of code that I called ServiceRunner. I put it up on NuGet and GitHub. but never got around to blogging about it until today. And since I’ve already blogged today about writing a Windows Service, it seemed a good time to share.
Why? Because I had grown tired of wiring up a Windows Service host for one project or another and wanted to reduce it down to the very least amount of code possible all while keeping the project as a standard console app to make debugging as simple and easy as possible.
Here is the easiest path to a working Windows Service:
- Create a .NET console app in Visual Studio.
- Install the NuGet package called ServiceRunner with Install-Package ServiceRunner.
- Add a class that inherits from ServiceRunner.ServiceRunnerInstaller as shown below.
- Add a simple bit of code to your console app’s Main method as shown below.
- Build and debug with the Runner’s runAsConsole constructor parameter set to false.
- When ready to deploy as a service, change that parameter to true. How you do that is up to you.
- Now run the InstallUtil command line installer as installutil c:\yourpath\yourapp.exe -i and your service is installed and ready to run. (If you use a Visual Studio command line, installutil will be in your path. Otherwise you’ll find it in the .NET framework install directory under C:\Windows\Microsoft.NET\Framework{64}\{version}.)
Here’s the code for the required installer class:
using ServiceRunner;
namespace ServiceRunnerDemo
{
/// <summary>
/// This class (name unimportant) must exist in your console app
/// for the installer to be recognized when you run installutil.exe
/// from the Windows\Microsoft.NET\Framework64\v4.0.30319 directory.
/// </summary>
public class MyInstaller : ServiceRunnerInstaller
{
protected override string ServiceName
{
get { return "ServiceRunner"; }
}
protected override string ServiceDescription
{
get { return "Service Runner description"; }
}
protected override string ServiceDisplayName
{
get { return "Service Runner"; }
}
protected override ServiceRunnerStartMode StartMode
{
get { return ServiceRunnerStartMode.Manual; }
}
protected override ServiceRunnerAccount Account
{
get { return ServiceRunnerAccount.LocalSystem; }
}
}
}
And here’s the code for the console app Main method.
using System;
using System.IO;
using ServiceRunner;
namespace ServiceRunnerDemo
{
class Program
{
static void Main(string[] args)
{
var logFile = "c:\\temp\\logit.txt";
var runner = new Runner("MyServiceRunnerDemo", runAsConsole: false);
runner.Run(args,
arguments =>
{
// equivalent of OnStart
File.WriteAllLines(logFile, new string[]
{
string.Format("args count: {0}", arguments.Length)
});
Console.WriteLine("args count: {0}", arguments.Length);
// normally you would launch a worker thread here
// to do whatever your service would do
File.WriteAllLines(logFile, new string[]
{
"start called"
});
Console.WriteLine("start called");
},
() =>
{
// equivalent of OnStop
File.WriteAllLines(logFile, new string[]
{
"stop called"
});
Console.WriteLine("stop called");
});
Console.ReadLine();
}
}
}
As you can see, the code is very simple. Far less to worry about than using the standard Visual Studio project template or trying to manually cobble up the installer and other pieces required. If you get any good use out of it, I would love to hear from you.
Happy Windows Service writing!