How to Create an Installer for a Windows Service Targeting .NET (.NET Worker Service)

Written by Renato Ivanescu · August 1st, 2025 · 9min read

Windows Services are long-running background applications that operate autonomously, without user interaction.

In .NET, you can create these services using two approaches: traditional .NET Framework Services or modern .NET Worker Services.

In this article I’ll walk you through the necessary steps to:

  • Create a .NET Worker Service that runs as a Windows service using Visual Studio.
  • Build an installer for the service using Visual Studio.
  • Explore an easier alternative for installer creation using the Advanced Installer tool.

1. Create the Worker Service

Let’s start by creating the Worker Service project:

1. In Visual Studio, go to File New Project.

2. Select Worker Service from the list of templates.

Create Worker Service

3. Go through the configuration dialogs to configure the service and select the appropriate .NET version for your scenario.

2. Configure the Service

Once the Worker Service project is created, it’s time to configure it to run as a Windows Service and implement the desired logic:

We need to update the Program.cs file to allow the application to run as a Windows Service.

We do this by calling the UseWindowsService() method in the host builder configuration.

This method configures the .NET Service to integrate with the Windows Service Control Manager to ensure it runs as a background service. Without it, the Worker Service would run as a console application rather than a real service.

Host.CreateDefaultBuilder(args)
    .UseWindowsService() 
    .ConfigureServices((hostContext, services) =>
    {
        services.AddHostedService<Worker>();
    })
    .Build()
    .Run();

NoteMake sure you have the Microsoft.Extensions.Hosting.WindowsServices NuGet package installed. It provides the UseWindowsService() method.

Now, modify the Worker.cs file to implement the logic for the long-running task.

For demonstration, let’s make the service create a text file on the desktop when started and append a message to it every five minutes.

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;
    public Worker(ILogger<Worker> logger)
    {
        _logger = logger;
    }
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        string filePath = @"C:\Users\ivane\Desktop\MyServiceStarted.txt";
        _logger.LogInformation("Worker starting at: {time}", DateTimeOffset.Now);
        try
        {
            // Check if the file exists. If not, create it with an initial message
            if (!File.Exists(filePath))
            {
                string initMessage = $"[{DateTime.Now}] Log file created.{Environment.NewLine}";
                await File.WriteAllTextAsync(filePath, initMessage, stoppingToken);
                _logger.LogInformation("Log file created at: {path}", filePath);
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error creating the log file.");
        }
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                string message = $"[INFO] MyService is running at {DateTime.Now}{Environment.NewLine}";
                await File.AppendAllTextAsync(filePath, message, stoppingToken);
                _logger.LogInformation("Message written to file at: {time}", DateTimeOffset.Now);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error writing to log file.");
            }
            await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
        }
    }
}

3. Publish the Worker Service

Before creating the installer, we need to publish the Worker Service project to bundle all the necessary runtime components:

- Right click on the project → Publish.

- Choose to publish the app to a local folder.

Publish Worker Service to local folder

- Specify the target path and click Publish.

4. Add the Visual Studio Installer Extension

Next, we need to create the installer for the service.

For this, you need to have the Visual Studio Installer Project extension installed.

  1. Go to Extensions Manage Extensions.
  2. Search for the Visual Studio Installer Projects under the Brows tab.
  3. Click Install and restart Visual Studio in order for the extension to be installed. Then, reopen Visual Studio.

NoteAlternatively, you can use the Advanced Installer extension for Visual Studio to create an installer directly in Visual Studio. You can also open the project in the Advanced Installer tool and have access to a lot of customization options for your package through an intuitive graphical interface.

5. Add the Setup Project

Now, let’s create the installer project that will package the Worker Service:

  1. Right-click on the solution → Add New Project.
  2. Select the Setup Project from the list of templates.
  3. Set a name for the project and click Create. The new project will appear in the Solution Explorer.

6. Add Project Output for the Setup Project

Once the setup project is created, it’s time to add the project output (the compiled files from the published Worker Service) to the Setup Project:

  1. Right click on the Setup ProjectView File System.
  2. Right click on the Application FolderAdd Project Output.
  3. In the opened dialog, select Publish Items and click OK.
Add Project Output

7. Add Service Installation Logic

The Visual Studio Installer Project does not natively support registration of services created as .NET Worker Service. It is designed for the .NET Framework services type. To work around this, we can create a helper executable that installs the Worker Service using sc.exe and then run that helper as a custom action during the install process.

NoteIf you’re using Advanced Installer to create a setup package for a Worker Service, there is no need to create a helper executable. Advanced installer automatically handles the service registration as part of its built-in functionality.

Let’s walk through how to create the service registration helper:

1. Right click on the solution → Add New Project.

2. Select Consol App (.NET Framework).

3. Add the next code to the Program.cs.

class Program
{
    static void Main(string[] args)
    {
        string exePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "MyWinService.exe");
        string serviceName = "MyWinService";
        string displayName = "My Windows Service";
        string description = "This service writes a log entry every 5 minutes.";
        try
        {
            // Create the service with display name
            Process.Start(new ProcessStartInfo("sc.exe", $"create {serviceName} binPath= \"{exePath}\" start= auto DisplayName= \"{displayName}\"")
            {
                UseShellExecute = false,
                CreateNoWindow = true
            })?.WaitForExit();
            // Set the service description
            Process.Start(new ProcessStartInfo("sc.exe", $"description {serviceName} \"{description}\"")
            {
                UseShellExecute = false,
                CreateNoWindow = true
            })?.WaitForExit();
            // Start the service
            Process.Start(new ProcessStartInfo("sc.exe", $"start {serviceName}")
            {
                UseShellExecute = false,
                CreateNoWindow = true
            })?.WaitForExit();
            // Log success         File.AppendAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "install_log.txt"),
                $"[{DateTime.Now}] Service {serviceName} created, described, and started.\n");
        }
        catch (Exception ex)
        {         File.AppendAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "install_log.txt"),
                $"[{DateTime.Now}] ERROR: {ex.Message}\n");
        }
    }
}

4. Next, you need to set the executable to run as Administrator:

4.1 Right-click the Console App project → Add New ItemApplication Manifest File.

4.2 In the generated manifest file, locate the <requestedExecutionLevel> tag and replace the line:

<requestedExecutionLevel level="asInvoker" uiAccess="false" />

with

<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />

5. Next, you need to create a helper to uninstall the service. For this, repeat the steps above as with the helper used for registration, but replace the code with the next one:

class Program
{
    static void Main(string[] args)
    {
        string serviceName = "MyWinService";
        try
        {
            // Stop the service
            Process.Start(new ProcessStartInfo("sc.exe", $"stop {serviceName}")
            {
                UseShellExecute = false,
                CreateNoWindow = true
            })?.WaitForExit();
            // Delete the service
            Process.Start(new ProcessStartInfo("sc.exe", $"delete {serviceName}")
            {
                UseShellExecute = false,
                CreateNoWindow = true
            })?.WaitForExit();
            // Optional logging
            File.AppendAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "uninstall_log.txt"),
                $"[{DateTime.Now}] Service {serviceName} stopped and deleted.\n");
        }
        catch (Exception ex)
        {
            File.AppendAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "uninstall_log.txt"),
                $"[{DateTime.Now}] ERROR: {ex.Message}\n");
        }
    }
}

8. Create Custom Actions to Register and Unregister the Service

Now it’s time to configure the Setup Project to execute your helpers at the appropriate stages of installation and uninstallation.

First, ensure the .exe files are included in the installer:

  1. Right-click on the Setup ProjectView File System.
  2. Right-click on the Application FolderAdd File.
  3. Navigate to the service install helper project’s bin\Debug (or Release) folder and select the .exe file.
  4. Repeat this step for your service uninstall helper project’s .exe file.
Add helpers to application folder

Once the .exe files are added to the installer folder, let’s create the custom actions:

1. For installation:

1. 1 Right click on the Setup ProjectView Custom Actions

1.2 Right click on the Install folderAdd Custom Action

1.3 Choose the service installer helper .exe file from the Application Folder.

Add custom action

1.4 In the Properties window, set the InstallerClass = False to ensure the exe is executed directly, and not looking for a specific Installer class within it.

Set InstallerClass to False

2. For uninstallation:

2.1 Right click on the Uninstall folder Add Custom Action

2.2 Choose the uninstall service helper .exe file from the Application Folder.

2.3 In the Properties window, set the InstallerClass = False.

9. Build and Install

Now build the installer and test it. Once the service installer is generated, run it.

You should notice the text file created on the desktop and the service appearing and running in the Service Manager Console. This indicator confirms the service has been successfully installed.

Services Listed in SMC

Creating the Installer with Visual Studio vs. Advanced Installer

When creating an installer for a service application, it’s worth considering a dedicated GUI-based packaging software like Advanced Installer. This tool simplifies the installer configuration – just add your service to a new project, and then configure everything directly within its intuitive graphical user interface. Advanced Installer also offers an extension for Visual Studio IDE.

Feature

Visual Studio

Advanced Installer

GUI-based configuration

Limited

Full-featured

Service register/unregistration

Not automatic - requires helper executables

Handled automatically


User Interface Customization

Minimal

Rich UI options: dialogs, themes, branding

Control over service operation parameters

Very limited - must be handled via custom code

Full control via GUI

Suitable for complex service configuration

Limited

Highly suitable

Conclusion

Creating a Windows Service with the .NET Worker Service is straightforward, but packaging it with the Visual Studio Installer Project extension requires a few extra steps.

Advanced Installer makes this much easier by providing built-in service management and an intuitive GUI for customization.

FAQ: How to Create an Installer for a Windows Service Targeting .NET

How do I create an installer for a .NET Windows Service?

You can create an installer using the Visual Studio Installer Projects extension by adding a Setup Project to your solution. For .NET Services, this approach requires additional steps such as creating helper executables and configuring custom actions to register the service.

Alternatively, you can use Advanced Installer, which provides a more streamlined solution. It has built-in support for service registration - no extra code or creating helper executables required.

Does the installer automatically register a .NET Windows Service?

The Video Studio Setup Project does not automatically register a .NET Windows Service as Windows Service. You must include and execute helper executables using custom actions to register/unregister the service. With Advanced Installer, service registration is handled automatically, with no need for additional code or helper apps.

How do I configure a .NET Worker Service to run as a Windows Service?

Update the Program.cs file to include the .UseWindowsService() method in the host builder. This integrates the application with the Windows Service Control Manager.

Can I create an installer for a Windows Service directly in Visual Studio?

Yes, you can do it using the Visual Studio Installer Project extension. However, for .NET Worker Services, you must add additional helper executables and configure custom actions for service registration and unregistration. Visual Studio does not provide built-in support for this as it does for traditional .NET Framework service.

Do I need separate executables to register/unregister a .NET Worker Service as a Windows Service?

When using Visual Studio you need to create and include two helpers: one for registering and starting the service and another for stopping and deleting the service during uninstallation. These must run as custom actions in the Setup Project. With Advanced Installer these extra steps are not needed.

Written by
See author's page
Renato Ivanescu

Renato is a technical writer for Advanced Installer and an assistant professor at the University of Craiova. He is currently a PhD. student, and computers and information technology are his areas of interest. He loves innovation and takes on big challenges.

Comments: