Debugging windows services

I’ve written a post before about how I debug windows service (i.e. run them from within Visual Studio without having to use “Attach to process”). I’ve come up with a new easier approach.

First of all, I’ve moved all logic from the service class. Instead I use a second class to control what should be started or not. So my Service class looks like this:

public partial class Service1 : ServiceBase
{
    private ApplicationRunner _runner = new ApplicationRunner();

    public Service1()
    {
        InitializeComponent();
    }

    protected override void OnStart(string[] args)
    {
        _runner.Start();
    }

    protected override void OnStop()
    {
        _runner.Stop();
    }
}

Hence I’m not dependent of the service class to be able to debug my application.

Now I just modify my Program.cs a bit:

internal static class Program
{
    [DllImport("kernel32")]
    private static extern bool AllocConsole();

    private static void Main()
    {
        if (Environment.CommandLine.Contains("-console") || Debugger.IsAttached)
        {
            if (Debugger.IsAttached)
                AllocConsole();

            var runner = new ApplicationRunner();
            runner.Start();
            Console.WriteLine("Press ENTER to quit.");
            Console.ReadLine();
            runner.Stop();
        }

        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[]
        {
            new Service1()
        };
        ServiceBase.Run(ServicesToRun);
    }
}

The great thing with that is that can now also run my services from a command prompt if they fail to start properly. I just do myservice.exe -console

The AllocConsole() creates a new console (making it an Console application instead of a windows service).

If you are interested, here is my ApplicationRunner which starts all of my services etc. It’s using my Griffin.Framework and autofac:

internal class ApplicationRunner
{
    private readonly ILog _logger = LogManager.GetLogger(typeof (ApplicationRunner));
    private AutofacServiceLocator _adapter;
    private ApplicationServiceManager _applicationServiceManager;
    private BackgroundJobManager _backgroundJobManager;

    public void BuildContainer()
    {
        var cb = new ContainerBuilder();
        cb.RegisterServices(Assembly.GetExecutingAssembly());
        var container = cb.Build();

        _adapter = new AutofacServiceLocator(container);
    }


    public void Start()
    {
        BuildContainer();

        _logger.Info("Starting application services.");
        _applicationServiceManager = new ApplicationServiceManager(_adapter);
        _applicationServiceManager.ServiceRestartFailed += OnAppServiceFailed;
        _applicationServiceManager.Start();

        _logger.Debug("Starting background jobs.");
        _backgroundJobManager = new BackgroundJobManager(_adapter);
        _backgroundJobManager.JobFailed += OnJobFailed;
        _backgroundJobManager.Start();
    }

    public void Stop()
    {
        _logger.Info("Stopping application services.");
        _applicationServiceManager.Stop();

        _logger.Info("Stopping background jobs.");
        _backgroundJobManager.Stop();
    }

    private void OnAppServiceFailed(object sender, ApplicationServiceFailedEventArgs e)
    {
        _logger.Error("Service failed.", e.Exception);
    }

    private void OnJobFailed(object sender, BackgroundJobFailedEventArgs e)
    {
        _logger.Error("Job failed.", e.Exception);
    }
}

A sample service can look like this:

[ContainerService(ContainerLifetime.SingleInstance)]
class QueueReader : ApplicationServiceThread
{
    private readonly IScopedActionInvoker _invoker;
    private ServiceBrokerService _ssb;

    public QueueReader(IScopedActionInvoker invoker)
    {
        _invoker = invoker;
        var queueName = ConfigurationManager.AppSettings["QueueName"];
        IQueueConfiguration config = new AppConfigQueueConfiguration(queueName)
        {
            MessageSerializer = new XmlMessageSerializer(new[] {typeof (MultiMessage), typeof (TrainInfo)})
        };

        _ssb = new ServiceBrokerService(config);
    }

    protected override void Run(WaitHandle shutdownHandle)
    {
        while (!shutdownHandle.WaitOne(0))
        {
            var msgs =_ssb.Receive(new RecieveSettings {MaxAmountOfMessages = 100, MaxWaitTime = TimeSpan.FromSeconds(1)});
            var messages = new List<MultiMessage>();
            foreach (var msg in msgs)
            {
                if (msg.MessageTypeName == ReceivedMessage.EndConversationType)
                {
                    _ssb.EndConversation(msg.Conversation);
                }
                else
                {
                    messages.Add((MultiMessage)msg.Body);
                }
            }
            _invoker.Execute<IMessageHandler>(x=>x.Process(messages));
        }
    }
}