This is the first of what I hope to be a multi-part deep-dive into building managed Windows services. As with any task the first thing we need to discover is the why and what for. Services can be very useful for a number of situations. On the client side of things one might be using them for a memory cache or offline cache of remote service calls. On the server side they are often used to move long-running or high-risk processes out of the IIS environment. In either the client or server they can also be used to elevate or suppress the access rights of the current user. Another use is running non-stop tasks that occur even when all users are signed out on the client. Regardless of your reasons for moving code OOP (Out-of-Process) into a service there are a few things that are difficult to get right. This series of posts attempts to address those not-so-obvious things that need to be done for a service. The following outline is what I intend to address:

  1. Building a service that can also be used from the console
  2. Proper event logging of service startup/shutdown and other activities
  3. Allowing multiple instances by using command-line arguments
  4. Self installation of service and event log
  5. Proper event logging of service exceptions and errors
  6. Controlling of start-up, shutdown and restart options
  7. Handling custom service commands, power, and session events
  8. Customizing service security and access control

Getting started on this rough sketch is really fairly easy. If you’ve been working in .NET for any period of time you have probably already created a service at some time. We are going to do that. Instead we are going to build the service project rather manually giving us the opportunity to discuss things as we go.

So let’s first just create a plain-old “Console Application”. By using a console application instead of the project template for a service we miss out on some of the built-in defaults.  That IMHO is not all bad.  Once created we are first going to focus on two primary concerns, 1) being able to ‘run’ the service in a console app, and 2) getting help on the command-line.  To accomplish this we are going to insist on at least one argument being provided, the command to run.  So we need a little help to dispatch those commands into the appropriate method to handle the request.  We will accomplish this by using a name/value lookup (IDictionary<,>) to turn command names into an action delegate.  Before we can do that however, we first need those action delegate methods.  So before we do anything with the Program class or Main function we are going to create a “Commands” class and stub out some methods like so:

static class Commands
{
    public static void Help(ICollection<string> args)
    { /* todo */ }

    public static void Run(ICollection<string> args)
    { /* todo */ }
}

I chose to use an ICollection<string> here instead of a string[] as it allows me to avoid a few unnecessary ToArray() calls. Now that we have this we can go back to the Program class and declare a static lookup table for dispatching commands. This is trivial with C#3′s dictionary initializer:

class Program
{
    private static readonly Dictionary<string, Action<ICollection<string>>> Actions
        = new Dictionary<string, Action<ICollection<string>>>(StringComparer.OrdinalIgnoreCase)
              {
                  {"Help", Commands.Help},
                  {"Run", Commands.Run},
              };

Easy enough right?  Should be obvious where I’m going with this to most of you.  All I need now is the Main function to lookup the command and dispatch to it.  If the lookup fails we will fall-back to the “Help” command.  The first thing to do withe the Main function is change it’s return type to int.  I’m not sure what got me in the habit of this, I guess it’s just my unease with using static data like Environment.ExitCode.  Anyway this is what my Main routine will look like for a while:

static int Main(string[] rawArgs)
{
    int exitCode = 1;
    try
    {
        List<string> args = new List<string>(rawArgs);
        string cmdName = String.Empty;
        if(args.Count > 0)
        {
            cmdName = args[0];
            args.RemoveAt(0);
        }

        Action<ICollection<string>> cmdAction;
        if (!Actions.TryGetValue(cmdName, out cmdAction))
            cmdAction = Commands.Help;

        cmdAction(args);

        exitCode = 0;
    }
    catch (System.Threading.ThreadAbortException)
    { }
    catch (Exception ex)
    {
        Console.Error.WriteLine(ex.ToString());
    }

    Environment.ExitCode = exitCode;
    return exitCode;
}

Next up is getting a stub implementation of the service class itself. Unfortunately ServiceBase is not always a very pleasant thing to work with from our console application. Due to this we are going to introduce our own class to mirror the service behavior’s available in the ServiceBase class. For starters though we will keep it simple and just get start/stop working. First we add a new C# class with the following methods:

class ServiceImplementation : IDisposable
{
    public void Start(ICollection<string> arguments)
    { /* todo */ }

    public void Stop()
    { /* todo */ }

    public void Dispose()
    { /* todo */ }
}

With this to stub the service calls we can now implement that Run method we added to the Commands class. This will be our entry-point when running from the console:

public static void Run(ICollection<string> args)
{
    using(Service.ServiceImplementation svc = new Service.ServiceImplementation())
    {
        svc.Start(args);

        Console.WriteLine("Press [Enter] to exit...");
        Console.ReadLine();

        svc.Stop();
    }
}

You should be able to compile and run at this point, not that there is any point in doing that :)

Continued on “Building a Windows Service – Part 2: Adding a Service to a Console Application

Comments