Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Custom service controller for debugging windows services

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".

Custom service controller project properties screenshot

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. 

Custom service controller form

Cross-posted from my personal weblog at Bob.Yexley.Net.

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

[Advertisement]

Comments

Brendan Tompkins wrote re: Custom service controller for debugging windows services
on 09-27-2006 3:55 PM

Very cool Bob...  I write a lot of windows services too, and what I end up doing is having a shell service project, which I know works just to do the timing, etc. and everything else lives in dll tiered project that I develop using NUnit and TDD.

I could see this being useful for more complicated services.   Nice!

Matt Casto wrote re: Custom service controller for debugging windows services
on 09-27-2006 3:59 PM
I've been doing something kind of like that for a while now, except instead of using the command line switch, I p/invoke GetConsoleMode which tells me whether the service was started from the SCM or not. This way you avoid having to modify your project file, and double clicking on the .exe from explorer will start the form instead of the service too.
Derik Whittaker wrote re: Custom service controller for debugging windows services
on 09-27-2006 4:30 PM

This is pertty cool...

One thing I have done in the pass is have the service either be a windows application or service.  The application (service) can make this decision at startup time.  We did this by passing in a command line arg for the type of start up we wanted.

This worked out nice because it allowed us to easly debug the service (in windows app mode) and then deploy the service as an actual service.

Rob Eisenberg wrote re: Custom service controller for debugging windows services
on 09-28-2006 7:11 AM

Awesome.  I will use this again and again and . . .

kiwidude wrote re: Custom service controller for debugging windows services
on 09-29-2006 6:15 AM
How about using Environment.UserInteractive rather than the command line argument? From my testing that seems to be false when run from the SCM and true in other scenarios. That way you can compile your executable as a winforms app without the background window popping up (required for the GetConsoleMode trick) and get the best of all worlds.
Jayanth G wrote re: Custom service controller for debugging windows services
on 10-02-2006 10:01 AM
Nice, but still takes a bit of work I guess. I generally add a console project to the existing solution and instantiate and start the service from there. Its just 2 lines of code and works like a charm.
WheelMUD - A C# MUD Server wrote WheelMUD Administration tools
on 04-13-2008 5:50 PM
WheelMUD - A C# MUD Server wrote WheelMUD Administration tools
on 04-13-2008 5:50 PM

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)