Recently I was trying to write a C# service using the .NET framework and ran into a lot of issues that were not covered in the documentation or if they were, the solution wasn't easily inferred. So I decided to write a series of articles describing in step-by-step detail, how I went about solving the problems I encountered.
This is a multi-article contribution which is broken down as follows:
Lets start off by creating a new C# project that is a console application. Visual Studio .NET does give you a wizard to create a Windows Service using C#, but I'll take it from scratch so that those who don't have VS.NET can still follow along. Figure 1 shows the initial project creation screen.

Figure 1
Now that we have a simple console application created, we need to change some
of the defaults that are created by the wizard. Lets open up the
Class1.cs file. This file contains the entry point for the application,
but we're going to rename the class Class to better reflect
it's role in the application. Right click the Class1.cs node in the
Solution Explorer and choose Rename. Rename the file
Application.cs. Then right click and choose View Code so that we
can edit the code directly. We're going to rename the class Class to Application.
We are also going to change the namespace to reflect where the class exists
within our company's namespace. I have a domain that I use for my sample code,
called CodeBlooded and I'm going to use this as my company
name. You are free to use any name that you want. Since this service is part of
a fictional Spades game (that I may end up fully developing over time), we are
going to name our namespace CodeBlooded.Spades.Services.
Edit the new Application.cs file and change the namespace from the
default of SpadesServer to CodeBlooded.Spades.Services.
To keep us from having to make these changes for every class we add to the
project, open up the project properties dialog by right clicking the
SpadesServer project in our solution and choose Properties. In the
project properties dialog, go to the Common Properties | General tab and
change the Application | Default Namespace field to CodeBlooded.Spades.Services. From now on, any class we create
will automatically be assigned to this namespace. Figure 2 shows you what this
tab should look like.

Figure 2
While we are in the project settings dialog, go to the Configuration Properties | Build tab and change the Outputs | XML Documentation File settings to SpadesServer.xml. This is an XML file that the compiler will create for us to help us build our documentation for the project. Look in the .NET framework SDK documentation on what documentation tags are supported. Figure 3 shows what this dialog should look like.

Figure 3
Now that we've taking care of some configuration details in time to get
started on actually creating some code. The .NET framework has a base class
ServiceBase that provides the interface to implement a
service. This class must be derived from, to add our code that actually has the
logic for the services we want to create. We must override the default methods
of the OnStart and OnStop methods.
These methods are called by the Service Control Manager to actually control the
service.
The problem is that the OnStart and OnStop methods must return control back to the Service Control
Manager within 1 minute, for the Service Control Manager to recognize that the
service is running or has stopped. So how do we actually get any work done, if
we must return within one minute. We must create a background thread that will
perform all the work that our service is going to do.
To keep from having to write the same code for each of the classes that we
will create during this tutorial, I'm going to create a base class that is
responsible for creating the worker thread as well as communicating with the
SCM. Create a new class that we'll call SpadesServiceBase.
The following code is what the class should look like.
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
namespace CodeBlooded.Spades.Services
{
public class SpadesServiceBase : System.ServiceProcess.ServiceBase
{
public SpadesServiceBase()
{
// TODO: Add any initialization code here
}
/// <SUMMARY>
/// Set things in motion so your service can do its work.
/// </SUMMARY>
protected override void OnStart(string[] args)
{
// TODO: Add code here to start your service.
}
/// <SUMMARY>
/// Stop this service.
/// </SUMMARY>
protected override void OnStop()
{
// TODO: Add code here to perform any tear-down
// necessary to stop your service.
}
}
}
We will eventually create 3 services to control the state of the game, with
an administration service that will control and/or manage the other 3 services.
To handle this, the administration service will derive from the SpadesServiceBase class but we'll create another class that
derives from SpadesServiceBase class that the 3 child
services will derive from. This helps us isolate the shared code better, by
having the lowest level base class contain only the code that is applicable to
all services, and the child services base class will contain the common code
that is shared across all child services.
So lets create another class called SpadesChildServiceBase and have it derive from SpadesServiceBase. Create the file any way you want, but the code
should end up looking like the following:
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
namespace CodeBlooded.Spades.Services
{
public class SpadesChildServiceBase : SpadesServiceBase
{
public SpadesChildServiceBase()
{
// TODO: Add any initialization code here
}
/// <SUMMARY>
/// Set things in motion so your service can do its work.
/// </SUMMARY>
protected override void OnStart(string[] args)
{
// TODO: Add code here to start your service.
base.OnStart( args );
}
/// <SUMMARY>
/// Stop this service.
/// </SUMMARY>
protected override void OnStop()
{
// TODO: Add code here to perform any
// tear-down necessary to stop your service.
base.OnStop();
}
}
}
Now we're going to write the code to create the background thread and the synchronization objects to be used by the SCM to start and stop the background thread. The background thread will periodically wake up and perform some action. If you were going to write a service that was dependant on messages coming from a client, then you use whatever IPC mechanism you wanted to control the communication. For best performance, you should make this code event driven instead of writing a poling mechanism. In future articles I may describe the way to accomplish this by using asynchronous socket calls, but for now I'm going to simply create a poling mechanism to manage the workflow of the services.
To facilitate the base classes communicating with the more specific derived
classes, the base class will provide a pure virtual method called Execute that will need to be implemented by the service. The
Execute method is called periodically by the base classes
as they periodically get signaled to run by our timer. The Execute method should contain the specific logic that you would
need to do periodically, to accomplish what each individual service is
responsible for.
We need to add a reference to System.Threading to our
SpadesServiceBase class and add a member variable of Thread that we'll call m_thread. We will
also need a ManualResetEvent from the Threading namespace to be used to communicate our stop request
from the SCM. In the OnStart method of the SpadesServiceBase, we'll create an instance of a Thread object as well as the ManualResetEvent. To run a thread, we need to define a thread
start method that is the entry point for the thread. In our case we'll call our
method ServiceMain and use a ThreadStart class from the framework to define our ServiceMain as a delegate method. Modify your SpadesServiceBase class to look like the following code
block.
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Threading;
namespace CodeBlooded.Spades.Services
{
public class SpadesServiceBase : System.ServiceProcess.ServiceBase
{
public SpadesServiceBase() {
// create a new timespan object
// with a default of 10 seconds delay.
m_delay = new TimeSpan(0, 0, 0, 10, 0 );
}
/// <SUMMARY>
/// Set things in motion so your service can do its work.
/// </SUMMARY>
protected override void OnStart(string[] args) {
// create our threadstart object to wrap our delegate method
ThreadStart ts = new ThreadStart( this.ServiceMain );
// create the manual reset event and
// set it to an initial state of unsignaled
m_shutdownEvent = new ManualResetEvent(false);
// create the worker thread
m_thread = new Thread( ts );
// go ahead and start the worker thread
m_thread.Start();
// call the base class so it has a chance
// to perform any work it needs to
base.OnStart( args );
}
/// <SUMMARY>
/// Stop this service.
/// </SUMMARY>
protected override void OnStop() {
// signal the event to shutdown
m_shutdownEvent.Set();
// wait for the thread to stop giving it 10 seconds
m_thread.Join(10000);
// call the base class
base.OnStop();
}
/// <SUMMARY>
///
/// </SUMMARY>
protected void ServiceMain() {
bool bSignaled = false;
int nReturnCode = 0;
while( true ) {
// wait for the event to be signaled
// or for the configured delay
bSignaled = m_shutdownEvent.WaitOne( m_delay, true );
// if we were signaled to shutdow, exit the loop
if( bSignaled == true )
break;
// let's do some work
nReturnCode = Execute();
}
}
/// <SUMMARY>
///
/// </SUMMARY>
/// <RETURNS></RETURNS>
protected virtual int Execute() {
return -1;
}
protected Thread m_thread;
protected ManualResetEvent m_shutdownEvent;
protected TimeSpan m_delay;
}
}
Now that we have these details taken care of, lets go ahead and create the
administrative service. Right now the service won't be responsible for a whole
lot, but over the next few lessons we'll add more functionality to it. For now
create a new class SpadesAdminService and derive it from
SpadesServiceBase. The code below is the boilerplate code
for the administrative service.
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
namespace CodeBlooded.Spades.Services
{
public class SpadesAdminService : SpadesServiceBase
{
public SpadesAdminService()
{
this.ServiceName = "SpadesAdminSvc";
}
/// <SUMMARY>
/// Set things in motion so your service can do its work.
/// </SUMMARY>
protected override void OnStart(string[] args)
{
base.OnStart( args );
}
/// <SUMMARY>
/// Stop this service.
/// </SUMMARY>
protected override void OnStop()
{
base.OnStop();
}
/// <SUMMARY>
///
/// </SUMMARY>
/// <RETURNS></RETURNS>
protected override int Execute() {
// for right now we'll just log a message in the
// Application message log to let us know that
// our service is working
System.Diagnostics.EventLog.WriteEntry("SpadesAdminSvc",
ServiceName + "::Execute()");
return 0;
}
}
}
Ok now that we have a service written, we need to do some more plumbing work
to communicate to Windows that we have services in this executable. To do this
is straightforward and all we need to do is create a collection of ServiceBase objects and simply call the Run
method of the base class ServiceBase. Add the following
code to the Main method in the Application object. The following code fragment shows all that is
necessary to start our services.
static void Main(string[] args)
{
// we'll go ahead and create an array so that we
// can add the different services that
// we'll create over time.
ServiceBase[] servicesToRun;
// to create a new instance of a new service,
// just add it to the list of services
// specified in the ServiceBase array constructor.
servicesToRun = new ServiceBase[] { new SpadesAdminService() };
// now run all the service that we have created.
// This doesn't actually cause the services
// to run but it registers the services with the
// Service Control Manager so that it can
// when you start the service the SCM will call
// the OnStart method of the service.
ServiceBase.Run( servicesToRun );
}
We're getting really close to wrapping this lesson up and all we need to do
to be able to run the service, is add our Installers. The
installers can either be called through a setup program that I may document in
some future article or they can be called by the InstallUtil program that
comes with the framework SDK.
Add a new installer to the project and we'll call it SpadesInstaller. After you run the Add Class wizard, we'll
add some code to the class constructor. The installers use attributes that are
applied to the class, where the installer will create an instance of each class
that has the RunInstaller attribute applied to the
class.
All we'll need is an instance of the ServiceProcessInstaller to control how the service process is
started and then for each service that we will be installing, we'll create a
ServiceInstaller object. After setting some properties on
these objects, we'll then add it to the Installers
collection. The following code shows all that we need to install our
service.
using System;
using System.Collections;
using System.ComponentModel;
using System.Configuration.Install;
using System.ServiceProcess;
namespace CodeBlooded.Spades.Services
{
/// <SUMMARY>
/// Summary description for SpadesInstaller.
/// </SUMMARY>
[RunInstaller(true)]
public class SpadesInstaller : System.Configuration.Install.Installer
{
public SpadesInstaller()
{
ServiceProcessInstaller process = new ServiceProcessInstaller();
process.Account = ServiceAccount.LocalSystem;
ServiceInstaller serviceAdmin = new ServiceInstaller();
serviceAdmin.StartType = ServiceStartMode.Manual;
serviceAdmin.ServiceName = "SpadesAdminSvc";
serviceAdmin.DisplayName = "Spades Administration Service";
// Microsoft didn't add the ability to add a
// description for the services we are going to install
// To work around this we'll have to add the
// information directly to the registry but I'll leave
// this exercise for later.
// now just add the installers that we created to our
// parents container, the documentation
// states that there is not any order that you need to
// worry about here but I'll still
// go ahead and add them in the order that makes sense.
Installers.Add( process );
Installers.Add( serviceAdmin );
}
}
}
After compiling to test our code, all we need to do is start a command prompt and move to our output directory. For now we'll just use the bin\Debug subdirectory of our project and run the following command to have our service installed.
installutil SpadesServer.exe
If you want to uninstall the service run the following command.
installutil SpadesServer.exe -U
Once this is complete, we should be able to start and stop the service from the Service Control Manager, to our hearts content.
Over the next few articles, we'll add more functionality to the framework that we've created here. We'll add some child services that when they start will start the admin service if it's not already started. The admin service will shutdown the child services if the admin service is stopped. This is similar to the way IISAdmin controls the different web services. In addition, we'll add a custom message log as well as a message log installer.