The first time I wrote a .NET windows service, I quickly discovered how tedious and painful it can be to debug it when you want to start testing its functionality. The whole process of installing the service using InstallUtil.exe, then starting the service through the standard windows service control monitor, then going back into Visual Studio, attaching to the process, setting up your breakpoints, then stopping the service and restarting it so you can debug what's going on in the OnStart event handler, etc, etc. What a mess. I kept telling myself there has GOT to be a better way to do this.
Well, there is...
A friend of mine that I used to work with (thanks Brian), and another that I still work with (thanks Dave) pointed me in the right direction, and I was able to get it working, and have since come to the point where, if you're developing a windows service, you MUST have something like this. It has literally saved me hours and hours of time. The idea is to add a windows form application to your service project that can act as the service control monitor for debugging. Here's how...
First thing you need to do is add a Windows Form class to your windows service project. This will be the form that you use to control the functions of the service, including starting and stopping it. Add a couple of buttons for starting and stopping the service (I'll explain what to wire the click events of those buttons up to in a minute), and then any other buttons you might want for calling and executing the various operations performed by your service to test them. Obviously, you can do pretty much whatever you want through this interface. This form basically is your service control monitor now.
Once your form is in place, you need to expose the OnStart and OnStop methods of your service class so that they can be called from your form. My code to do this looks something like this:
/// <summary>
/// Starts the service
/// </summary>
public void StartService()
{
OnStart( new string[] { } );
}
/// <summary>
/// Stops the service
/// </summary>
public void StopService()
{
OnStop();
}
Of course, you can wrap/expose any other functionality you want to be able to call or control from your form as well. Options for that are limited only by your own personal creativity. Those are the methods that I call from the OnClick event handlers for the buttons that I put on my form. Again, you can add buttons and expose any functionality of your service that you wish to manually test through this form in this same way.
The next step is to create a command line switch that can be passed into the entry point of your service, to tell entry point to launch your form, rather than the service itself. The command line switch can basically be anything you want it to be. The command line arguments will be parsed and responded to appropriately in the entry point. I used the "-interactive" switch myself:
namespace My.Service
{
/// <summary>
/// The main entry point for the service
/// </summary>
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main( string[] args )
{
ProcessSwitches( args );
}
/// <summary>
/// Loop through the command line arguments passed into the application, and parse each switch
/// </summary>
/// <param name="args">String array of command line switches to parse</param>
private static void ProcessSwitches( string[] args )
{
// If command line arguments are specified, then loop through them and parse them
if( args.Length > 0 )
{
for( int i = 0; i < args.Length; i++ )
{
ParseSwitch( args[ i ] );
}
}
// If no command line arguments are passed, parse null to just run the service
else
{
ParseSwitch( null );
}
}
/// <summary>
/// Parse the command line switch and execute the appropriate method based on the switch
/// </summary>
/// <param name="cmdSwitch">The command line switch to parse</param>
private static void ParseSwitch( string cmdSwitch )
{
switch( cmdSwitch.ToLower() )
{
// Run in interactive mode
case "-interactive":
{
RunInteractive();
break;
}
// Just run the service
default:
{
ServiceBase[] ServicesToRun;
// More than one user Service may run within the same process. To add
// another service to this process, change the following line to
// create a second service object. For example,
//
// ServicesToRun = new ServiceBase[] {new Service1(), new MySecondUserService()};
//
ServicesToRun = new ServiceBase[] { new BackupService() };
ServiceBase.Run( ServicesToRun );
break;
}
}
}
private static void RunInteractive()
{
Application.EnableVisualStyles();
Application.Run( new InteractiveDebugger( new BackupService() ) );
}
}
}
Once you've got the command line switch working, now you just need to setup your project so that when you click "Start" from within Visual Studio, your debugger form gets launched. This can be done by specifying the command line switch to pass into the entry point of your service when you Start the application in debug mode. To do this, from within Visual Studio, right-click on your service project and select "Properties" to display the project properties dialog. In this dialog, select the "Debug" tab. On this tab, there is a sub-section called "Start Options" which contains a text field labeled "Command line arguments:", which is the field you want to set. Enter the value of the command line switch that you specified in your code, in my case it was "-interactive".

Once you've set that property and saved your changes, now, when you run/debug your service, it will startup with your new custom service controller form, and you will be able to set breakpoints and test the functionality of your service all you want, without ever having to use InstallUtil.exe for installing and testing your service again.

Posted
09-27-2006 2:30 PM
by
Bob Yexley