Note! This was written for Topshelf 2.2.2.0 and things might have changed in newer versions.
Topshelf
Topshelf (http://topshelf-project.com) is a framework for building Windows services. The service can either be standalone or one that is hosted by Topshelf. The latter style is called shelving (and the service you write is called a shelf) and it enables xcopy deploy of your service, instead of stop service, uninstall, copy, install, start.
Debugging a shelf
The problem with using the shelving style for your service is debugging. The documentation explains how to do it (http://topshelf-project.com/debugging-a-topshelf-shelf/), but it’s not that neat as it involves modifying where your build outputs the dlls.
An easier way, at least for me, would be to debug the shelf service by starting it from a console app, i.e. as a standalone service, without any need to modify where the output from the build goes. Ideally the console app would be as simple as possible, just reusing the code from the shelf.
A shelf service is initiated and configured by a bootstrapper (a class that inherits the interface Bootstrapper<T>
) but unfortunately, if you compare the configuration for a standalone service with the shelf bootstrapper, they are not configured the same way, so reusing the configuration from the shelf bootstrapper in a standalone service seems not possible, but it’s actually quite easy.
Reuse the bootstrapper
Instead of implementing Bootstrapper<T>
derive from this class instead:
public abstract class BootstrapperBase<T> : Bootstrapper<T> where T : class { void Bootstrapper<T>.InitializeHostedService(IServiceConfigurator<T> cfg) { InitializeHostedService(cfg); } public abstract void InitializeHostedService(ServiceConfigurator<T> cfg); }
Instead of implementing
void InitializeHostedService(IServiceConfigurator<T>
you implement
void InitializeHostedService(ServiceConfigurator<T>
The bootstrapper in the example at http://topshelf-project.com/documentation/shelving/ becomes (changes are highlighted)
public class AShelvedClockBootstrapper : BootstrapperBase<TheClock> { public override void InitializeHostedService(ServiceConfigurator<TheClock> cfg) { cfg.ConstructUsing(n => new TheClock()); cfg.WhenStarted(s => { XmlConfigurator.Configure(new FileInfo(Path.Combine( AppDomain.CurrentDomain.BaseDirectory, "clock.log4net.config"))); s.Start(); }); cfg.WhenStopped(s => s.Stop()); } }
In the console app you start the service like this:
static void Main(string[] args) { XmlConfigurator.ConfigureAndWatch(new FileInfo(".\\log4net.config")); HostFactory.Run(x => { x.Service<TheClock>(
s => new AShelvedClockBootstrapper().InitializeHostedService(s)); x.RunAsLocalSystem(); x.SetServiceName("TheClock"); }); }