Windows Services with .NET Framework

Posted by Joe Enos on June 08, 2021 · 9 mins read

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('"');
}
}
}

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