We all know what a Windows Service is - a long running executable that's designed to work without user interaction. They can be configured to start when the system boots and they can be run without any users logged into the system. This tutorial is going to provide step-by-step instructions on how to build a Windows Service using C# and .NET.
The first thing you're going to want to do is create a new Console Application in Visual Studio. I like to start building services as console applications so they can easily be tested and debugged before converting them to services.
In order to build services, we depend on some .NET objects location in the System.ServiceProcess and System.Configuration.Install assemblies. Go ahead and add those references to your project.
When you created the project, Visual Studio should have created a file called Program.cs, which looks something like this:
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyWindowsService
{
class Program
{
static void Main(string[] args)
{
}
}
}
The only thing we need to do in order to make this class into a service is make Program extend System.ServiceProcess.ServiceBase.
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceProcess;
namespace MyWindowsService
{
class Program : ServiceBase
{
static void Main(string[] args)
{
}
}
}
By extending ServiceBase, we're given a bunch of service related functions we can override. At a minimum, every service should override OnStart and OnStop. These are where, as the names imply, your custom logic should go when the service is started and stopped.
{
static void Main(string[] args)
{
}
protected override void OnStart(string[] args)
{
base.OnStart(args);
//TODO: place your start code here
}
protected override void OnStop()
{
base.OnStop();
//TODO: clean up any variables and stop any threads
}
}
There are several other functions beyond start and stop like pause, continue, and shutdown, but they're not needed for a basic service. For all available functions, check out the ServiceBase member list on MSDN.
Now we need to add some information about our service - like a name. Let's add a constructor to Program and put it there.
{
static void Main(string[] args)
{
}
public Program()
{
this.ServiceName = "My Service";
}
protected override void OnStart(string[] args)
{
base.OnStart(args);
//TODO: place your start code here
}
protected override void OnStop()
{
base.OnStop();
//TODO: clean up any variables and stop any threads
}
}
The constructor is where you'd also set lots of other information about your service (if the default settings wouldn't work). These can include things like which events it can handle, what operation it can do (pause, stop, etc.), and what event log it should log information to (application, system, etc.). For us, however, the default settings will work just fine.
We're very close to having a finished service. All that left is to tell Windows what service to run when your application it executed. Just like normal applications, execution begins in the Main function. This is where we'll create an instance of our service and tell it to run.
{
static void Main(string[] args)
{
ServiceBase.Run(new Program());
}
public Program()
{
this.ServiceName = "My Service";
}
protected override void OnStart(string[] args)
{
base.OnStart(args);
//TODO: place your start code here
}
protected override void OnStop()
{
base.OnStop();
//TODO: clean up any variables and stop any threads
}
}
That's it! The service implementation is complete. We can't install it yet though, because we haven't implemented an installer. To do that we need to add another class to our project called MyWindowsServiceInstaller.
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyWindowsService
{
class MyWindowsServiceInstaller
{
}
}
When Visual Studio creates a class for you, it doesn't automatically make it public. It's very important that this class be made public. When you install a service, the installer will look through your assembly for public classes with a specific attribute. If it's not public, it won't find it.
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyWindowsService
{
public class MyWindowsServiceInstaller
{
}
}
This class needs to extend System.Configuration.Install.Installer and be given a RunInstaller attribute. This is the attribute that the service installer looks for when installing your service.
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration.Install;
using System.ComponentModel;
namespace MyWindowsService
{
[RunInstaller(true)]
public class MyWindowsServiceInstaller : Installer
{
}
}
Now we need to configure how we want our service installed. We'll do this in the constructor for the class we just created.
public class MyWindowsServiceInstaller : Installer
{
public MyWindowsServiceInstaller()
{
var processInstaller = new ServiceProcessInstaller();
var serviceInstaller = new ServiceInstaller();
//set the privileges
processInstaller.Account = ServiceAccount.LocalSystem;
serviceInstaller.DisplayName = "My Service";
serviceInstaller.StartType = ServiceStartMode.Manual;
//must be the same as what was set in Program's constructor
serviceInstaller.ServiceName = "My Service";
this.Installers.Add(processInstaller);
this.Installers.Add(serviceInstaller);
}
}
This is pretty much the bare minimum when it comes to service options. We have to create a
ServiceProcessInstaller and a ServiceInstaller. These two classes are responsible for installing the service. The ServiceProcessInstaller installs information common to all services, and the ServiceInstaller installs information for this specific service.
The Account property sets which privileges you'd like the service to run under. The default is User, but then we'd have to specify a username and a password to determine the account. I chose LocalSystem, which gives the service a lot (probably too much) access to the local computer.
Services are identified by name, so you have to ensure serviceInstaller.ServiceName is exactly the same as what you set in the constructor of Program. The rest of the options are pretty obvious. All that's left is to add our two installers to the Installers collection.
We're done! All that's left to do now is install the service.
Installing The Service
Services are installed using a tool from Microsoft called installutil.exe. This tool is free and is probably already on your computer. I doubt it's in your system path, so you might want to do a quick search for it and add it to your environment variables before continuing.
All you have to do to install your service is open a command prompt, cd to your Release directory, and type:
Your service is now installed and ready to go. If you bring up your services manager, you should see one labeled "My Service". Of course, if you start it, nothing will happen since we didn't actually put any code in the OnStart function.
If you want to uninstall the service, you can do so with the same utility but with the "-u" flag.
There's a lot of power that we haven't even touched in Windows Services, but this tutorial should hopefully give you a starting point on which you can expand and build some much more complex solutions. All of the example code has been attached as a Visual Studio 2008 solution.
02/26/2009 - 09:40
I know that's not the point of your post, but i'd like to add one more thing about windows services regarding debug.
Create a class library in which you can put all your business logic; this way you can debug the class library with a simple console application, and then after all the tests are done, you add the reference to it on your service project.
02/26/2009 - 10:28
That's a good suggestion. Thanks!
03/23/2009 - 21:50
Sometimes you just need to get in and debug the service itself to tell what its doing. I include the following code snippet at the beginning of the OnStart method
System.IO.Directory.SetCurrentDirectory(System.AppDomain.CurrentDomain.BaseDirectory);
// Look for the file 'Debug.txt' in the Service Directory. If it exists, give the user the option
// to launch the debugger
if ( ReflectionUtil.IsDebugAssembly(this.GetType().Assembly) && File.Exists("Debug.txt") )
System.Diagnostics.Debugger.Launch();
And here is the static method in my ReflectionUtil class
/// Checks if the given assembly is a debug assembly
/// </summary>
/// <param name="assm">An Assembly object of the assembly</param>
/// <returns>True if the assembly can be debugged. False otherwise</returns>
public static bool IsDebugAssembly(Assembly assm)
{
foreach (object attr in assm.GetCustomAttributes(false))
{
if (attr is DebuggableAttribute)
{
return ((DebuggableAttribute)attr).IsJITTrackingEnabled;
}
}
return false;
}
Basically, if the assembly of the service was compiled in debug mode and in the app directory of the service I find a file called Debug.txt, launch the debugger. If the service is compiled in release mode, it will never go into the debugger. And now I can control if the debugger starts by just touching/removing a text file. I've found this to be extremely useful in hunting down problems.
06/23/2009 - 18:59
I am able to install the service and it is visible in Services window. When I click Start button, it shows a few progress bars and then stops with an error message: Error 1083: The executable program that this service is configured to run in does not implement this service.
I have installed in on my Windows XP Pro. My account has Administrators rights.
How can I fix the error please?
08/10/2009 - 07:55
How to show pop up message when service starts running.
Is there any code, please send it to me (email: emsraju@gmail.com)
Thanks
-Sunil
08/10/2009 - 08:20
By default, services aren't allowed to create user interface elements. You can enable this by going to Services in Computer Management and viewing the Properties for your service. Go to the Log On tab and check "Allow service to interact with desktop". Once you do that, you should be able to create a normal MessageBox. Here's a tutorial that covers MessageBoxes.
11/28/2009 - 16:38
Hi, I got the same msg that Piyush Varma :
1083: The executable program that this service is configured to run in does not implement this service.
The service suppose to connect to an Imap server and do 2 or 3 operation. I use the Mailbee .Net Object Lib.
Can someone help me with that issue?
Thanks
Jean-Christophe Fortin
web: jeanchristophe.fortin@gmail.com
Add Comment
[language] [/language]
Examples:
[javascript] [/javascript]
[actionscript] [/actionscript]
[csharp] [/csharp]
See here for supported languages.
Javascript must be enabled to submit anonymous comments - or you can login.