Windows Services are usefull for long running background tasks. In this post we will create a basic service that runs a scheduled task. We will also learn how to add a ProjectInstaller and how to install the service from the command line using InstallUtil.
1. Create Windows Service project
Open Visual Studio and create a new Windows Service Project from the Windows Desktop templates.
In the image above we have named the service TestService and we are using Visual Studio 2013 with .NET Framework 4.5.1 to create the project. After clicking OK you should be presented with a project structure like this.
The image above shows the project structure of the created TestService. Notice there has been a service automatically added to the project called Service1.
2. Rename the Windows service
Service1 is not a very good name for the service and should be changed to something more meaningful. You can do this by right clicking on the service and clicking rename. For this demonstration we will rename the service to ScheduledTaskService.
3. Add an installer to the project
We now need to add a ProjectInstaller to the project, so that the ScheduledTaskService can be installed at the command line using InstallUtil.
If you try to install the service without the ProjectInstaller, you will get an error message that says: No public installers with the RunInstallerAttribute.Yes attribute could be found.
To add a ProjectInstaller to the project, right click on the ScheduledTaskService design surface and then click Add Installer. The animation below shows you how to do this.
There is one more thing you need to do, before you can install the service, and that is to change the service run Account from User to LocalSystem. If you don’t do this, you will be asked to specify a user account when installing the service.
To change the account type under which the service will run, click on serviceProcessInstaller1 and then change the Account property from User to LocalService. See image below.
4. Configure the Service Name, Description and StartType
Next we will need to configure the service name and description so that you can identify your service in the list of Windows services. We will also change the start type to Automatic so that the service starts when Windows starts.
To configure these properties click on serviceInstaller1 and then modify the properties as shown in the image below.
The image below, shows what our installed service will look like, in the list of Windows services.
You should now be able to build the TestService and install it from the command line using InstallUtil, but before doing this, we will add some basic functionality to the service.
5. Scheduled task code
If we were to install the service in its current state, it would install and could be started or stopped but it wouldn’t do anything.
We will prove the service works by writing some code that makes the service log some text to a file every minute. We will do this by adding a Timer
to the service and hooking up the OnStart
and OnStop
events to start and stop the Timer
.
Right click on the ScheduledTaskService file, click View Code, and then modify the code to look like the following.
public partial class ScheduledTaskService : ServiceBase
{
private readonly Object _lock = new Object();
private readonly Timer _timer;
private readonly Stopwatch _stopwatch;
private readonly string _logPath;
private bool _started;
// 1 minute tick interval
private const int TickInterval = 60000;
public ScheduledTaskService()
{
InitializeComponent();
_timer = new Timer(Tick);
_stopwatch = new Stopwatch();
_logPath = String.Format(@"{0}\log.txt", AppDomain.CurrentDomain.BaseDirectory);
}
protected override void OnStart(string[] args)
{
_started = true;
// trigger timer to run first log now
_timer.Change(0, Timeout.Infinite);
// stopwatch is used to calculate how much time to take off the next tick interval
// this is useful for long running processes when you need an exact tick time
_stopwatch.Restart();
Log("OnStart");
}
protected override void OnStop()
{
_started = false;
Log("OnStop");
}
public void Tick(Object state)
{
_stopwatch.Restart();
// do your service processing here:
// write log entry to log file
Log("Tick");
// when stopped don't trigger next tick interval
if (_started)
{
_timer.Change(Math.Max(0, TickInterval - _stopwatch.ElapsedMilliseconds), Timeout.Infinite);
}
}
private void Log(string text)
{
lock (_lock)
{
File.AppendAllText(_logPath, String.Format("{0} | Test Service | {1}{2}", DateTime.Now, text, Environment.NewLine));
}
}
}
There’s not much to explain in the code above. It’s just a basic implementation using a threading Timer
. The lock
is required because the Tick
function runs on a different thread to the OnStart
and OnStop
functions. Without the lock
, the service will throw an access denied exception.
6. How to install the Windows Service from the command line
Create a new folder in C:\Program Files called Test Service, then build the project and copy the contents of the Debug or Release folder into the Test Service folder.
Open the command prompt as an administrator and change the directory to C:\Windows\Microsoft.NET\Framework\v4.0.30319
Now run the following command to install the service.
InstallUtil.exe -i "C:\Program Files\Test Service\TestService.exe"
You can uninstall the service by running the following command.
InstallUtil.exe -u "C:\Program Files\Test Service\TestService.exe"
You should now see the Test Service in the list of Windows services. This can be found by running the command: services.msc
at the command prompt. The service will not be started by default, so right click on the service, then click Start.
Open the log.txt file located at C:\Program Files\Test Service\log.txt and you should see a log entry for each minute the service has run.
Now you can test starting and stopping the service and you will see log entries in the log.txt file. Try stopping the service, deleting the log.txt file, and then restarting your machine. You will see that the service starts when Windows starts because of the service StartType property being set to Automatic.
And that’s how you create a basic Windows service.
Obviously it’s a bit of a pain copying files and using InstallUtil to install a Windows service, so in another post I will explain how to create a set-up project to install a Windows service.