I have written a Windows service, of which I want to have 1 instance running per customer. This is because the customers each have their own DB with identical schemas; the
You can pass parameters to your installer using installutil, for example ServiceName and DisplayName.
ProjectInstaller.cs
public partial class ProjectInstaller : Installer
{
protected override void OnBeforeInstall(IDictionary savedState)
{
SetServiceName();
base.OnBeforeInstall(savedState);
}
protected override void OnBeforeUninstall(IDictionary savedState)
{
SetServiceName();
base.OnBeforeUninstall(savedState);
}
private string AppendParameter(string path, char parameter, string value)
{
if (!path.StartsWith("\""))
path = $"\"{path}\"";
if (value.Contains(" "))
value = $"\"{value}\"";
return $"{path} -{parameter}{value}";
}
private void SetServiceName()
{
if (Context.Parameters.ContainsKey("ServiceName"))
serviceInstaller.ServiceName = Context.Parameters["ServiceName"];
if (Context.Parameters.ContainsKey("DisplayName"))
serviceInstaller.DisplayName = Context.Parameters["DisplayName"];
Context.Parameters["assemblypath"] = AppendParameter(Context.Parameters["assemblypath"], 's', serviceInstaller.ServiceName);
}
}
This will append a parameter to the path stored with the service, for example:
Before: "C:\Service.exe"
After: "C:\Service.exe" -s"Instance 1"
You can then read this parameter when you start the service and pass to your services constructor.
Program.cs
static void Main(string[] args)
{
string serviceName = args.Single(x => x.StartsWith("-s")).Substring("-s".Length);
ServiceBase service = new Service(serviceName);
ServiceBase.Run(service);
}
Service.cs
public partial class Service : ServiceBase
{
public Service(string serviceName)
{
InitializeComponent();
ServiceName = serviceName;
}
}
Usage
installutil /ServiceName="Instance 1" /DisplayName="Instance 1 Service" "C:\Service.exe"
installutil /ServiceName="Instance 2" /DisplayName="Instance 2 Service" "C:\Service.exe"