Mark Pearce writes about Debugging a .Net Windows Service from within the IDE. We do something similar with our C++ Windows services but, as you’ve probably come to expect from me, it’s slightly more complicated than Mark’s approach.
Similarly to Mark I allow the main thread of the application to call the service entry point rather than passing the work off to the Service Control Manager (SCM) when we want to run the service in ‘debug mode’. We decide if we run in debug mode by passing a command line switch, /debug, in some servers this switch is only valid in debug builds, in others it’s useful to have it available all the time.
When a service is in ‘debug mode’, running as a normal exe rather than via the SCM, we need to intercept the normal service state change notifications that the service might generate rather than simply passing them on to the SCM. Basically we’re not allowed to call
SetServiceStatus() when running in debug mode. This is made easier by the fact that we have a service base class that passes the derived class (which is where each particular service’s code lives) an interface for it to use to notify the SCM of things. Our derived class needn’t know that we’re running in debug mode because it never calls into the SCM directly, it calls via the interface that it’s been given and we can simply decide to ignore the calls when running in debug mode.
The final wrinkle was allowing the service to respond to pause, resume and stop requests. When you’re running the exe as a service you can control it using the services control panel. It’s useful to have the same functionality available when testing the service so that you can debug issues with service shutdown or pause/resume functionality. It also means that you can run the service under tools such as BoundsChecker or Purify and cleanly shut the service down so that you can produce valid reports. These kinds of controls are usually passed to the service via the callback function that you pass into
RegisterServiceCtrlHandler(). The handler that our service code uses is already decoupled from the main service logic via an internal message queue (implemented with an IOCP) and when in debug mode we simply feed service control messages into the IOCP from a source other than the SCM callback. This ‘other source’ is simply a thread that listens on a couple of named Win32 event objects. These events can be controlled by a simple gui and allow us to send shutdown and pause/resume controls to the service even when it’s running in debug mode.
The result of all of this is that we can run our services as normal exes by supplying a command line switch. The code that we need to write for each service never needs to know or care that it’s running in debug mode and we can control the service as normal even when it’s not connected to the SCM.