TopShelf is a framework you can use to build windows services which can be installed easily. It is used by such famous products as
NServiceBus. It allows you to create windows services that you don't have to install with "installutil.exe". The windows service you create is self installing.
When using TopShelf, you install your windows service by running the following command:
Acme.TopShelfExample.Windows.Service.exe install
Which would result in the following console output:
Configuration Result:
[Success] Name ExampleWorker
[Success] DisplayName Example Worker
[Success] Description An Example Worker.
[Success] ServiceName ExampleWorker
Topshelf v3.0.105.0, .NET Framework v4.0.30319.269
Running a transacted installation.
Beginning the Install phase of the installation.
Installing Example Worker service
Installing service ExampleWorker...
Service ExampleWorker has been successfully installed.
Creating EventLog source ExampleWorker in log Application...
The Install phase completed successfully, and the Commit phase is beginning.
The Commit phase completed successfully.
The transacted install has completed.
Which is pretty much the same as you get using "installutil.exe".
To uninstall, run the following:
Acme.TopShelfExample.Windows.Service.exe uninstall
Which would result in the following console output:
Configuration Result:
[Success] Name ExampleWorker
[Success] DisplayName Example Worker
[Success] Description An Example Worker.
[Success] ServiceName ExampleWorker
Topshelf v3.0.105.0, .NET Framework v4.0.30319.269
The uninstall is beginning.
Uninstalling ExampleWorker service
Removing EventLog source ExampleWorker.
Service ExampleWorker is being removed from the system...
Service ExampleWorker was successfully removed from the system.
The uninstall has completed.
Again, the same as you'd get using "installutil.exe".
To use TopShelf for your windows service, create a console application and install TopShelf using NuGet:
install-package TopShelf
The following code should get you started:
namespace Acme.TopShelfExample.Windows.Service
{
using Topshelf;
internal class Program
{
internal static void Main(string[] args)
{
HostFactory.Run(hostConfigurator =>
{
hostConfigurator.Service<ExampleWorker>(serviceConfigurator =>
{
serviceConfigurator.ConstructUsing(name => new ExampleWorker());
serviceConfigurator.WhenStarted(ew => ew.Start());
serviceConfigurator.WhenStopped(ew =>
{
ew.Stop();
// And dispose or release any component containers (e.g. Castle)
// or items resolved from the container.
});
});
hostConfigurator.RunAsLocalSystem();
hostConfigurator.SetDescription("An Example Worker.");
hostConfigurator.SetDisplayName("Example Worker");
hostConfigurator.SetServiceName("ExampleWorker"); // No spaces allowed
hostConfigurator.StartAutomatically();
});
}
}
}
namespace Acme.TopShelfExample.Windows.Service
{
using System;
using System.Globalization;
using System.Threading;
public abstract class Worker
{
private Thread thread;
private bool stop = true;
protected Worker()
{
this.SleepPeriod = new TimeSpan(0, 0, 0, 10);
this.Id = Guid.NewGuid();
}
public TimeSpan SleepPeriod { get; set; }
public bool IsStopped { get; private set; }
protected Guid Id { get; private set; }
public void Start()
{
string logMessage = string.Format(CultureInfo.CurrentCulture, "Starting worker of type '{0}'.", this.GetType().FullName);
System.Diagnostics.Debug.WriteLine(logMessage);
this.stop = false;
// Multiple thread instances cannot be created
if (this.thread == null || this.thread.ThreadState == ThreadState.Stopped)
{
this.thread = new Thread(this.Run);
}
// Start thread if it's not running yet
if (this.thread.ThreadState != ThreadState.Running)
{
this.thread.Start();
}
}
public void Stop()
{
string logMessage = string.Format(CultureInfo.CurrentCulture, "Stopping worker of type '{0}'.", this.GetType().FullName);
System.Diagnostics.Debug.WriteLine(logMessage);
this.stop = true;
}
protected abstract void DoWork();
private void Run()
{
try
{
try
{
while (!this.stop)
{
this.IsStopped = false;
this.DoWork();
Thread.Sleep(this.SleepPeriod);
}
}
catch (ThreadAbortException)
{
Thread.ResetAbort();
}
finally
{
this.thread = null;
this.IsStopped = true;
string logMessage = string.Format(CultureInfo.CurrentCulture, "Stopped worker of type '{0}'.", this.GetType().FullName);
System.Diagnostics.Debug.WriteLine(logMessage);
}
}
catch (Exception e)
{
string exceptionMessage = string.Format(CultureInfo.CurrentCulture, "Error running the '{0}' worker.", this.GetType().FullName);
System.Diagnostics.Debug.WriteLine(exceptionMessage, e);
throw;
}
}
}
}
namespace Acme.TopShelfExample.Windows.Service
{
using System.Diagnostics;
public class ExampleWorker : Worker
{
protected override void DoWork()
{
Debug.WriteLine("Example Worker is doing something.");
}
}
}
And that is it.