Windows services

This code demonstrates two simple techniques for Windows services in .NET. First, it shows how to create a Console application which also can be installed as a Windows service - this way you can write a Windows service and easily test and debug it without installing it, and without using multiple projects. Second, it shows how to set the service name at install-time instead of compile-time.

Create the project as a Console application, rather than a Windows service. Then just add the following code, and organize your codebase as you wish.

using System;
using System.ComponentModel;
using System.Configuration.Install;
using System.Linq;
using System.ServiceProcess;

namespace ConsoleApp1 {
    public partial class Service1 : ServiceBase {
        static void Main(string[] args) {
            var service = new Service1();
            if (Environment.UserInteractive) {
                try {
                    service.OnStart(args);
                } catch (Exception ex) {
                    Console.WriteLine(ex);
                } finally {
                    Console.WriteLine("DONE - Press <ENTER> to exit.");
                    service.OnStop();
                    Console.ReadLine();
                }
            } else {
                Run(service);
            }
        }

        protected override void OnStart(string[] args) { /* implementation */ }
        protected override void OnStop(){ /* implementation */ }
    }

    [RunInstaller(true)]
    public partial class ProjectInstaller : Installer {
        public ProjectInstaller() {
            string[] commandlineArgs = Environment.GetCommandLineArgs();

            string servicename;
            string servicedisplayname;
            ParseServiceNameSwitches(commandlineArgs, out servicename, out servicedisplayname);

            _serviceProcessInstaller = new ServiceProcessInstaller();
            _serviceInstaller = new ServiceInstaller();

            _serviceInstaller.ServiceName = servicename;
            _serviceInstaller.DisplayName = servicedisplayname;

            Installers.AddRange(new Installer[] { _serviceProcessInstaller, _serviceInstaller });
        }

        private ServiceProcessInstaller _serviceProcessInstaller;
        private ServiceInstaller _serviceInstaller;

        private void SetServicePropertiesFromCommandLine(ServiceInstaller serviceInstaller) {
            string[] commandlineArgs = Environment.GetCommandLineArgs();

            string servicename;
            string servicedisplayname;
            ParseServiceNameSwitches(commandlineArgs, out servicename, out servicedisplayname);

            serviceInstaller.ServiceName = servicename;
            serviceInstaller.DisplayName = servicedisplayname;
        }

        // http://www.runeibsen.dk/?p=153
        private void ParseServiceNameSwitches(string[] args, out string name, out string displayName) {
            var nameSwitch = args.FirstOrDefault(x => x.StartsWith("/servicename"));
            var displayNameSwitch = args.FirstOrDefault(x => x.StartsWith("/servicedisplayname"));

            if (nameSwitch == null)
                throw new ArgumentException("Argument 'servicename' is missing");
            if (displayNameSwitch == null)
                throw new ArgumentException("Argument 'servicedisplayname' is missing");
            if (!(nameSwitch.Contains('=') || nameSwitch.Split('=').Length < 2))
                throw new ArgumentNullException("The /servicename switch is malformed");

            if (!(displayNameSwitch.Contains('=') || displayNameSwitch.Split('=').Length < 2))
                throw new ArgumentNullException("The /servicedisplaynameswitch switch is malformed");

            name = nameSwitch.Split('=')[1];
            displayName = displayNameSwitch.Split('=')[1];

            name = name.Trim('"');
            displayName = displayName.Trim('"');
        }
    }
}

Installing it from the command line:

installutil -i /servicename="my service" /servicedisplayname="my display name" ConsoleApp1.exe