Introduction
Windows services are handy things to have around. Unfortunately, Microsoft doesn't make it simple to write your own. What is more, they can be difficult to debug during development.
This article presents a simple framework for creating your own Windows services. It accepts several command line parameters to make installing, starting, stopping, and uninstalling the service a snap. Also, it has a console mode to make debugging easy.
Background
I spent a lot of time wading through pages and pages, site after site, of example code for developing Windows services. None of the samples out there really did everything I wanted. Some employed considerable hacks, and there were none that separated the service bits from the actual worker bits. This project contains what I consider to be the bare minimum features for a usable Windows service, with a simple interface for adding your worker code.
Using the Code
This is setup for Visual Studio 2008 and .NET 3.5. Some modifications would be needed to use this code with .NET 2.0, e.g., get rid of lambda functions, but it could easily be done.
ISimpleServiceWorker
All you have to do is inherit the ISimpleServiceWorker
interface and implement it in your worker class.
using System;
namespace RPW.Simple
{
interface ISimpleServiceWorker
{
void Init();
void Run();
void Cleanup();
}
}
Initialize your resources in the Init()
method and clean up your resources in the Cleanup()
method. The Run()
method would typically contain a loop that manages the state of your task. You don't have to create a thread; the SimpleProject
class will create the necessary thread. You should keep in mind that the thread will be terminated by the Thread.Abort()
method. The ThreadAbortException
will be caught in the SimpleProject
class that creates the thread context, so you don't need to catch it, but you could if you have a reason. In order to keep the shutdown time reasonable, try not to make blocking calls, e.g., sockets.
Console Mode
Services are notoriously difficult to debug. For this reason, this project provides for a console mode so that you can run your code without all of the service overhead. Simply pass the -console command line switch to your service executable, and Shazaam! You're running in console mode. In console mode, the static WriteLog
method of the SimpleService
class directs logging to the console window instead of the application event log.
Managing Your Service
The SimpleService
class provides several command line switches:
PS C:\rev\src\simpleservice\src\bin\release> .\SimpleService.exe -help
= usage:
= SimpleService -install == install service
= SimpleService -uninstall == uninstall service
= SimpleService -start == start service
= SimpleService -stop == stop service
= SimpleService -status == get the current status of the service
= SimpleService -console == run in console mode
= SimpleService -help == show this help message
PS C:\rev\src\simpleservice\src\bin\release>
Using these command line switches, you can install and uninstall your service without using InstallUtil.exe. Also, you can start and stop your service once it is installed. The -status switch will tell you if your service is installed, started, or stopped.
Nuances
The project uses the process name as the service name. You could change this in code, but you must make sure that the same name is applied to the ServiceBase
and the ServiceInstaller
. For simplicity, use the static ServiceName
property of the SimpleServiceInstaller
class.
The project assumes that your service needs LocalSystem privileges and starts automatically. This will invoke the Windows Vista and Windows 7 UAC dialogs when you use the service. You can change those defaults in the SimpleServiceInstaller
class.
public SimpleServiceInstaller()
{
m_processInstaller = new ServiceProcessInstaller()
{
Account = ServiceAccount.LocalSystem
};
Installers.Add(m_processInstaller);
m_serviceInstaller = new ServiceInstaller()
{
StartType = ServiceStartMode.Automatic,
ServiceName = ServiceName,
DisplayName = ServiceName,
Description = ServicePath
};
Installers.Add(m_serviceInstaller);
}
Conclusions
I think you will be pleased with this design. Simply implement a worker class by inheriting the ISimpleServiceWorker
interface, change the project assembly name, and build it! The command line switches make everything else a snap. Please don't hesitate to notify me of bugs or oversights. And by all means, enjoy!
History
- January 2010 - Initial submission.