DEV Community

Cover image for Creating a Windows Service using .NET and OpenMSI
Open MSI Support
Open MSI Support

Posted on

Creating a Windows Service using .NET and OpenMSI

In this article, you'll create a .NET Worker Service, configure it to run as a Windows Service, package it using OpenMSI, and generate a Windows Installer (MSI) that automatically registers and starts the service during installation.

Our Windows Service Example

Prerequisites

Before starting, ensure you have:

  • Visual Studio 2022 or later
  • .NET 8 SDK or later
  • Windows 10/11

First things first, let's create a .NET Worker Service application that will generate the executable associated with the Windows Service we want to deploy.

On Visual Studio startup window click on 'Create a new project' then search for 'worker' template and select the C# option:

Visual Studio start up window

Let's be creative and name our application as 'WindowsServiceExample':

Worker service selection

Click on 'Next' then click on 'Create' in the next dialog.

Worker service project creation

With our project created we now want to make it Windows specific so we can change the service name displayed in Windows Service console programatically. Open the NuGet package manager clicking with a right click over the project and click on 'Packages' in the context menu, then search and add the 'Microsoft.Extensions.Hosting.WindowsServices' NuGet package in 'Browse' tab.

Windows Services package

Change the initial Program.cs file so it looks like this:

namespace WindowsServiceExample
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = Host
                .CreateDefaultBuilder(args)
                .UseWindowsService(options =>
                {
                    options.ServiceName = "WindowsServiceExample";
                })
                .ConfigureServices(services => {
                    services.AddHostedService<Worker>();
                });

            var host = builder.Build();
            host.Run();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

The UseWindowsService extension integrates the worker application with the Windows Service Control Manager (SCM). The ServiceName value is the internal service name used by Windows when registering and managing the service.

The next step is configuring a publish profile so we can easily publish our application files. Right click on 'WindowsServiceExample' project and click on 'Publish', select the 'Folder' option then click 'Next' and 'Finish' in the next dialog.

Publish dialog

Now configure the publish profile settings clicking on 'Show all settings', in 'Profile settings' dialog set the application to be 'Self-contained' so the application includes the .NET runtime and does not require .NET to be installed on the target machine. Also, configure the application to target 'win-x64' runtime (Windows 64 bits). Click on 'Save' and publish the application.

Profile settings

The initial logic present in Worker.cs is good enough for this example, so we are done with our application, it is time to work on our installer.

Creating the Installer using OpenMSI

We will use OpenMSI to create our installer (don't worry, OpenMSI is free for non commercial and Open Source projects).

OpenMSI is a .NET tool, that means you can install it running:

dotnet tool install openmsi --global
Enter fullscreen mode Exit fullscreen mode

Once the installation finishes, it is time to initialize the installer files using OpenMSI, to do that in the same directory of WindowsServiceExample.csproj file run:

openmsi init 
Enter fullscreen mode Exit fullscreen mode

This command will prompt some information about the package and application:

Init prompts

Once finished, the installer package files are created, the only one we need to edit is msi-package.yaml to configure the 'package' and 'services' sections so it looks like this:

# yaml-language-server: $schema=https://openmsi.dev/schemas/openmsi-package-v1.schema.json
package:
  productName: WindowsServiceExample
  manufacturer: John Doe
  description: A simple Windows Service example
  version: 1.0.0
  productCode: fdb96b96-8b1c-4021-b9f4-3cd19cc8e67e
  upgradeCode: d9e86ff3-9a68-4b86-b351-860c65a4290b
  productIconSource: msi-package\assets\demoappmulti.ico

dialogs:
  - source: msi-package

assets:
  - name: OpenMSI_BmpTopBanner
    source: msi-package\assets\top-banner-370x44.bmp
  - name: OpenMSI_BmpBackground
    source: msi-package\assets\background-370x270.bmp
  - name: OpenMSI_IcoInfo
    source: msi-package\assets\icon-info.ico
  - name: OpenMSI_IcoWarning
    source: msi-package\assets\icon-warn.ico
  - name: OpenMSI_BmpFolderNew
    source: msi-package\assets\folder-new.bmp
  - name: OpenMSI_BmpFolderUp
    source: msi-package\assets\folder-up.bmp

files:
  - source: bin\release\net10.0\publish\win-x64
    installFolder: true

services:
  - name: WindowsServiceExample
    displayName: Windows Service Example
    description: A simple Windows Service example
    start: auto
    account: LocalSystem
    executable: WindowsServiceExample.exe
Enter fullscreen mode Exit fullscreen mode

Here a summary describing the services section in more detail:

Property Description
name Internal service identifier
displayName Displayed in Services.msc
description Service description shown in Windows
start Startup type (auto, manual, disabled)
account Service execution account
executable Executable registered as the service

For simplicity this example runs under LocalSystem. Production services should use the least privileged account possible, such as LocalService, NetworkService, or a dedicated service account.

Now we just have to run:

openmsi build
Enter fullscreen mode Exit fullscreen mode

OpenMSI build output

At this point you can open the Windows Service Console (services.msc) and verify the installed service:

Installation process

Or you can execute:

Get-Service WindowsServiceExample
Enter fullscreen mode Exit fullscreen mode

Common Questions

  • Does the service start automatically?
  • Does it stop during uninstall?
  • Is it removed during uninstall?

OpenMSI automatically generates the Windows Installer service entries required to install, start, stop, and remove the service during the installation/removal lifecycle.

Troubleshooting

The service does not appear in Services.msc

Verify that the executable path specified in executable matches the published file name.

The service fails to start

Check Windows Event Viewer for startup errors.

MSI installs successfully but the service is not created

Ensure the services section is present in msi-package.yaml and has a valid name property.

References

Windows Service Example GitHub Repository
OpenMSI: Deploy on Windows with a modern, simple, and intuitive tool
Worker Services - .NET | Microsoft Learn

Top comments (2)

Collapse
 
suppdevbot profile image
DEV SUPPORTS •
You need to verify your account.
Enter fullscreen mode Exit fullscreen mode

tr.ee/dev-to

Collapse
 
openmsisupport profile image
Open MSI Support •

Scam reported.