Windows Service Hosting

Component: NServiceBus
NuGet Package NServiceBus (7.x)

Running an NServiceBus endpoint inside a Windows service is the most common approach to hosting.

Example Windows Service hosting

  • Create a new Console Application.
  • Reference System.ServiceProcess.dll.
  • Change the program to inherit from ServiceBase
class ProgramService :
    IEndpointInstance endpointInstance;

    static void Main()
        using (var service = new ProgramService())
            if (ServiceHelper.IsService())
            Console.WriteLine("Bus started. Press any key to exit");

    protected override void OnStart(string[] args)

    async Task AsyncOnStart()
        var endpointConfiguration = new EndpointConfiguration("EndpointName");
        endpointInstance = await Endpoint.Start(endpointConfiguration)

    protected override void OnStop()

    Task AsyncOnStop()
        if (endpointInstance == null)
            return Task.CompletedTask;
        return endpointInstance.Stop();
Since Environment.UserInteractive always returns true on .NET Core, ServiceHelper.IsService() is used to provide a dual console/service experience. That is, this process can be executed from the command line or run as a Windows service. Note that if there is no intention to target .NET Core, then Environment.UserInteractive can be used.
static class ServiceHelper
    public static bool IsService()
        using (var process = GetParent(Process.GetCurrentProcess()))
            return process != null && process.ProcessName == "services";

    static Process GetParent(Process child)
        var parentId = 0;

        var handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

        if (handle == IntPtr.Zero)
            return null;

        var processInfo = new PROCESSENTRY32
            dwSize = (uint) Marshal.SizeOf(typeof(PROCESSENTRY32))

        if (!Process32First(handle, ref processInfo))
            return null;

            if (child.Id == processInfo.th32ProcessID)
                parentId = (int) processInfo.th32ParentProcessID;
        } while (parentId == 0 && Process32Next(handle, ref processInfo));

        if (parentId > 0)
            return Process.GetProcessById(parentId);
        return null;

    static uint TH32CS_SNAPPROCESS = 2;

    public static extern bool Process32Next(IntPtr hSnapshot, ref PROCESSENTRY32 lppe);

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern IntPtr CreateToolhelp32Snapshot(uint dwFlags, uint th32ProcessID);

    public static extern bool Process32First(IntPtr hSnapshot, ref PROCESSENTRY32 lppe);

    public struct PROCESSENTRY32
        public uint dwSize;
        public uint cntUsage;
        public uint th32ProcessID;
        public IntPtr th32DefaultHeapID;
        public uint th32ModuleID;
        public uint cntThreads;
        public uint th32ParentProcessID;
        public int pcPriClassBase;
        public uint dwFlags;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public string szExeFile;


When self-hosting a Windows Service, the startup code is in full control of installation. Windows supports these features though the use of the Service Control tool. For example a basic install and uninstall commands would be:

sc.exe create SalesEndpoint binpath= "c:\SalesEndpoint\SalesEndpoint.exe"
sc.exe delete SalesEndpoint

For completeness, here are some other common usages of the Service Control tool:

Setting the Windows Service name

The Windows Service name can be configured at creation time, as follows:

sc.exe create [ServiceName] binpath= [BinaryPathName]
sc.exe create SalesEndpoint binpath= "c:\SalesEndpoint\SalesEndpoint.exe"

Setting the Display name

The display name can be configured, at creation time, using the displayname argument:

sc.exe create [ServiceName] displayname= [Description] binpath= [BinaryPathName]
sc.exe create SalesEndpoint displayname= "Sales Endpoint" binpath= "c:\SalesEndpoint\SalesEndpoint.exe"

Setting the Description

The description can be changed, after the Windows Service has been created, using the sc description command.

sc.exe description [ServiceName] [Description]
sc.exe description SalesEndpoint "Service for hosting the Sales Endpoint"

Specifying Service dependencies

The dependencies of a Windows Service can be configured after it has been created using the sc config command.

sc.exe config [ServiceName] depend= <Dependencies(separated by / (forward slash))>
sc.exe config SalesEndpoint depend= MSMQ/MSDTC/RavenDB

Setting the Restart Recovery Options

Windows has a Windows Service recovery mechanism that makes sure a crashed process will be restarted.

The endpoint can fail when using the NServiceBus Host or when self-hosting and implementing a critical error handler that exits the process in case a critical error occurs.

If Windows Service Recovery is not configured, message processing will halt. Therefore it's important to configure recovery options when hosting an NServiceBus endpoint as a Windows Service.

The recovery options can be adjusted via the Services dialog or via sc.exe. Note that the command line tool has advanced configuration options.

Configuring Windows Service Recovery via sc.exe

The default restart duration is 1 minute when enabling recovery via the Windows Service Management Console, but a different restart duration may be defined for the subsequent restarts using sc.exe.

The following example will restart the process after 5 seconds the first time, after 10 seconds the second time and then every 60 seconds. The Restart Service Count is reset after 1 hour (3600 seconds) of uninterrupted work since the last restart.

sc.exe failure [ServiceName] reset= [seconds] actions= restart/[milliseconds]/restart/[milliseconds]/restart/[milliseconds]
sc.exe failure SalesEndpoint reset= 3600 actions= restart/5000/restart/10000/restart/60000

Configuring Service Recovery via Windows Service properties

Open the services window, select the endpoint Windows Service and open its properties. Then open the Recovery tab to adjust the settings:

Windows Service properties Recovery tab

Restart durations are only configurable using sc.exe.

Username and password

Username and password can be configured at creation time using the obj and password parameters.

sc.exe create [ServiceName] obj= [AccountName] password= [Password] binpath= [BinaryPathName]
sc.exe create SalesEndpoint obj= MyDomain\SalesUser password= 9t6X7gkz binpath= "c:\SalesEndpoint\SalesEndpoint.exe"

Start mode

The Windows Service start mode can be configured at creation time using the start parameter.

sc.exe create [ServiceName] start= {auto | demand | disabled} binpath= [BinaryPathName]
sc.exe create SalesEndpoint start= demand binpath= "c:\SalesEndpoint\SalesEndpoint.exe"


A Windows Service can be uninstalled using the sc delete command.

sc.exe delete [ServiceName]
sc.exe delete SalesEndpoint

Compared to NServiceBus Host

Code similarity

When using a self-hosted approach inside a Windows Service, this code will share many similarities with other hosting code such as send-only clients and web service hosting. This similarity will result in more consistent (and hence easier to understand) code and increased opportunities for code re-use.


Self-hosting is a specific solution to a problem that can be more specialized and has fewer dependencies. This results in

  • Reduced memory usage
  • Faster startup/debugging time
  • Smaller deployment size


The NServiceBus Host is a non-trivial piece of software, especially when including its dependency on TopShelf. As such the NServiceBus Host can add complexity to debugging issues. Self-hosting uses fewer layers of abstraction which results in a simpler debugging experience.

Controlling the entry point

When using the NServiceBus Host, the host is calling the endpoint configuration code. As such, the configuration code and behaviors (such as startup and shutdown) need to plug into very specific APIs. For example IWantCustomLogging, IWantCustomInitialization, IWantToRunWhenEndpointStartsAndStops and IConfigureLogging. If the scenario is inverted, i.e. developer code calls NServiceBus configuration, then the requirement for these APIs no longer exists.


Related Articles

Last modified