[This example is for .NET 3.5 and Visual Studio 2008]

I recently had to fix a bug in a Windows service written in C#. Unfortunately, Windows services are not straightforward to run in debug mode. This led me to try two solutions that I found lacking. I ended up using a relatively simple and flexible method involving reflection. I will provide some sample code below to help you use this in your own projects.

Option 1: the Microsoft way (inconvenient)

Microsoft’s official recommendation is to install the service and then attach the debugger to the service. While this is the best way to monitor a live service, it has two drawbacks when it comes to development: 1) you need to reinstall the service each time you want to debug, and 2) “you cannot debug the code in the service’s OnStart method this way”. Note that Microsoft does offer a workaround for the latter problem, though I find it to be rather inconvenient:

[...] create a temporary second service in your service application that exists only to aid in debugging. You can install both services, and then start this “dummy” service to load the service process. Once the temporary service has started the process, you can then [...] attach to the service process.

After trying this method, I ended up getting frustrated by the extra work involved and chose a simpler, but less maintainable, solution.

Option 2: run the service as an application (unmaintainable)

What ended up working for me (for a while, at least) was converting the project into a console application, with the service code being called from Main(). I successfully used this method to find the cause of the error and test my fix. I also used this method to make enhancements to the service.

However, it was only when I was making frequent check-ins that I spotted my problem: it took effort to make sure that I kept the repository’s version as a service and my own version as an application. It shouldn’t take that much effort to keep code synchronized.

So, while this method allows you to debug a service without first installing it, as well as debug the OnStart method, it doesn’t make for a maintainable solution. After learning about reflection, I found a better way.

Option 3: writing a test application using reflection

This method is based on reflection’s ability to invoke non-public members from outside of the target application. The basic idea is to create a second application that initiates the service.

Here’s how you can set this up for yourself.

First, create a Windows Service project. Visual Studio will automatically generate some code for you. Note the OnStart and OnStop methods, inherited from ServiceBase. These get called by Windows when the service is started and stopped, respectively; you will need to call these methods when you simulate running the service.

using System;
using System.ServiceProcess;

namespace TestableWindowsService
{
  public partial class Service1 : ServiceBase
  {
    public Service1()
    {
      InitializeComponent();
    }

    protected override void OnStart(string[] args)
    {
    }

    protected override void OnStop()
    {
    }
  }
}

Once your service is set up, create a new Console Application in the same solution. You will need to add a reference to the service project, as well as a reference to System.ServiceProcess (available under the .NET tab in the Add Reference… screen).

There isn’t much code you have to write to simulate calling the service, but it’s worth explaining.

Service1 service = new Service1();

Initiate the service so we can call its methods.

Type service1Type = typeof (Service1);

This is the key to reflection. Once we have the Type for the class, we can use reflection to access its methods and properties at runtime.

MethodInfo onStart = service1Type.GetMethod("OnStart", BindingFlags.NonPublic | BindingFlags.Instance);

Here we use the GetMethod method to search for the method we want. The BindingFlags are important because OnStart is an instance method that is protected. Setting these flags ensures that these types of methods are included in the search. If the method isn’t found, null is returned.

onStart.Invoke(service, new object[] {null});

This is where we actually call the method. The method is called using the service object created above. The second argument to this method is the string[] parameter required by OnStart. Note that this cannot be set to null if there is a parameter in the method signature.

At this point, we have effectively started the service. If there is any code in your service’s Program.Main method that is supposed to run, you would call that now. Note that you can’t actually call the Program.Main method because it will try to start the service, which you can’t do from within Visual Studio.

MethodInfo onStop = service1Type.GetMethod("OnStop", BindingFlags.NonPublic | BindingFlags.Instance);
onStop.Invoke(service, null);

This code will stop the service. Since OnStop doesn’t have any parameters, null is used for the second argument. Depending on how your service is configured, you may not actually need to call this (since the TestApplication will kill the service anyway at this point).

using System;
using System.Reflection;

namespace TestableWindowsService
{
  class TestProgram
  {
    static void Main()
    {
      Service1 service = new Service1();

      Type service1Type = typeof (Service1);

      MethodInfo onStart = service1Type.GetMethod("OnStart", BindingFlags.NonPublic | BindingFlags.Instance);
      onStart.Invoke(service, new object[] {null});

      MethodInfo onStop = service1Type.GetMethod("OnStop", BindingFlags.NonPublic | BindingFlags.Instance);
      onStop.Invoke(service, null);
    }
  }
}

When you build and run the test application in debug mode, you will be able to step through your service code without going through the trouble of installing the service and attaching the debugger.