Saturday, January 9, 2010

How to Write a .NET Windows Service with Console Mode

Windows Services are applications that run in the background and provide some functionality to the system. They have virtually no user interface – except Windows’ built in “start” and “stop” functionality and a few settings to control security, etc.  This makes them great for standalone processes that ‘just work’ and require no user intervention, but it also makes them notoriously difficult to debug and develop.  Because of this, I typically create two projects when creating windows services – the windows service for production, and a console app for development.  However, recently, I came up with a way to combine the two, so that I can have both the windows service AND the console app in the same project.  Here’s what I did:

First, in general, it’s good to separate your actual logic into a separate class library that contains the bulk of your service logic.  I name this something like “MyCompany.App”, and generally have one class that the Windows Service can use fairly easily– that is, it has a Start and Stop method.

   1: public class MyService{
   2:    public void Start(){//Tip:BackgroundWorkers are great here!...}
   3:    public void Stop(){...}
   4: }

Next, add a Windows Service project and in the ServiceBase code that is generated, new up the above class and use it:

   1: public partial class MyServiceWindowsService : ServiceBase
   2: {
   3:    private MyService service;
   4:  
   5:    public AlertFmMonitoringStationService()
   6:    {
   7:        InitializeComponent();
   8:    }
   9:  
  10:    protected override void OnStart(string[] args)
  11:    {
  12:        this.service = new MyService();
  13:        service.Start();           
  14:    }
  15:  
  16:    protected override void OnStop()
  17:    {
  18:        service.Stop();
  19:    }
  20: }

Add a ProjectInstaller (open the service in design mode, view properties, and click ‘Add Installer’), and you have the windows service, ready for installation with installutil.exe.   To wire up the console app, a few more steps are required.  See, Windows Services are .exes, just like any other app – they just happen to follow a certain convention that makes them Windows Services (namely, they have a ProjectInstaller and one or more instances of ServiceBase).   As long as we follow that convention, Windows is happy, and we can do whatever else we want.  So:

  • Right click the Windows Service project and click ‘properties’
  • Set Output type to ‘Console Application’, and Startup Object to the ‘Program’ class.
  • Under ‘Debug’ enter ‘-console’ in the ‘Startup Options’ ‘Command Line Arguments’ section
  • Inside the ‘Program’ class, add a switch to enable console mode
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main(string[] args)
{           
  if (args.Contains("-console"))
  {
      System.Console.WriteLine("press 'q' to quit.");
      var app = new MyService();
      app.Start();
      while (Console.ReadKey().KeyChar != 'q')
      {
      }
      app.Stop();
  }
  else
  {                
      ServiceBase[] ServicesToRun;
      ServicesToRun = new ServiceBase[]
                          {
                              new MyServiceWindowsService()
                          };
      ServiceBase.Run(ServicesToRun);
  }
}

And that’s it!  The end result is a command line app that can be run using the ‘-console’ switch, or installed as a Windows Service.  When debugging locally, it’s run as a console app.  Ideally, you would build a Setup package to install it, but to install manually, you would use the installutil tool that ships with .NET.