.Net Core 2.0 Windows Service

42,041

Solution 1

It is now possible to write a Windows Service in .NET Core 2.0 without third-party libraries, thanks to the release of the Windows Compatibility Pack (at the time of writing, still in prerelease). As the page itself warns:

But before you start porting, you should understand what you want to accomplish with the migration. Just porting to .NET Core because it's a new .NET implementation isn't a good enough reason (unless you're a True Fan).

In particular, writing a Windows Service in .NET Core may now be possible, but you will not get cross-platform compatibility out of the box, because the assemblies for platforms other than Windows will just throw a PlatformNotSupportedException if you attempt to use service code. Working around this is possible (using RuntimeInformation.IsOSPlatform, for example), but that's another question altogether.

Also, third-party libraries may still offer a nicer interface with regards to installing the service: as of writing, the current version of the compatibility pack (2.0.0-preview1-26216-02) does not support the System.Configuration.Install namespace, so the default approach with a ServiceProcessInstaller class and installutil will not work. More on that later.

With all that said, let's suppose you have created a brand new Windows service (Service1) from the project template (not strictly required since it contains nothing interesting, other than a class inheriting from ServiceBase). All you need to do to make it build on .NET Core 2.0 is to edit and replace the .csproj with the new format:

<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp20</TargetFramework>
    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.Windows.Compatibility" Version="2.0.0-*" />
  </ItemGroup>
</Project>

And then delete properties\AssemblyInfo.cs since it's no longer required and will conflict with version information in the project itself.

If you already have a service and it has dependencies, the conversion may be more complicated. See here.

Now you should be able to run dotnet publish and get an executable. As mentioned, you can't use the ServiceProcessInstaller class to install the service, so you'll have to manually

  • register the event source the service uses;
  • create the actual service.

This can be done with some PowerShell. From an elevated prompt in the location that contains your published executable:

$messageResourceFile = "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\EventLogMessages.dll"
New-EventLog -LogName Application -Source Service1 -MessageResourceFile $messageResourceFile
sc.exe create Service1 binPath= (Resolve-Path .\WindowsService1.exe)

This is not ideal in several ways: this hard-codes the path of the message resource file (we should really be determining where it is from the executable and the runtime paths in the registry), and it hard-codes the service name and executable name. You may want to give your project its own installation capabilities by doing some command-line parsing in Program.cs, or use one of the libraries mentioned in Cocowalla's answer.

Solution 2

To host .NET Core 2.0 Web API as Windows Service. I followed this guide Host ASP.NET Core in a Windows Service. The Prerequisites part is unclear to me. After some mistakes, here is what I did: Source Code

  1. Create an ASP.NET Core Web Application enter image description here
  2. Choose API enter image description here
  3. Edit .csproj file, need to change target framework from netcoreapp2.0 to net461, explicitly list all the package references rather than using Microsoft.AspNetCore.All, as following

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net461</TargetFramework>
    <RuntimeIdentifier>win7-x64</RuntimeIdentifier>
    <!--<TargetFramework>netcoreapp2.0</TargetFramework>-->
  </PropertyGroup>

  <ItemGroup>
    <Folder Include="wwwroot\" />
  </ItemGroup>

  <ItemGroup>
    <!--<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.6" />-->
    <PackageReference Include="Microsoft.AspNetCore" Version="2.0.2" />
    <PackageReference Include="Microsoft.AspNetCore.Hosting.WindowsServices" Version="2.0.2" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.3" />
    <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.2" />
    <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.1" />
    <PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="2.0.2" />
  </ItemGroup>

  <ItemGroup>
    <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.3" />
  </ItemGroup>

</Project>
  1. power shell [solution-folder] dotnet publish -o "[publish-folder]"
  2. power shell [solution-folder] sc.exe create CoreApi binpath="[publish-folder]\CoreApiHostedAsWindowsService.exe"
  3. power shell [solution-folder] sc.exe start CoreApi
  4. visit default api power shell [solution-folder] Invoke-WebRequest http://localhost:5000/api/values

Solution 3

I'll summarise some options:

  1. Move your code into a .NET Standard library, and host it in a .NET Framework app, so you can use ServiceBase. This will of course need the .NET Framework to be installed on the target machine
  2. Use NSSM (the Non-Sucking Service Manager) to manage a .NET Core console app (it has a public domain license)
  3. Use Windows API calls to hook into Windows service methods. This is the approach taken by DotNetCore.WindowsService and dotnet-win32-service (both are MIT licensed)

I think @JeroenMostert's comment is a bit harsh - I can see the appeal of not being dependant on a particular .NET Framework version being available on the target machines. Plenty others obviously feel the same, as the 2 repos I linked to are rather popular.

Solution 4

In .NET Core 2.1 you are able to use the Host and HostBuilder to get a console applicaiton that runs as a service. If you containerize your console application you can deploy the container anywhere and it is just the same as running as a service. You are able to use the Host and HostBuilder to manage DI, Logging, Graceful shut down, etc in you console app. Have a look at:

Hosting services in .NET Core console application

Solution 5

An easy way to create a .NET Core Windows service is by using Peter Kottas' DotNetCore.WindowsService library.

The NuGet package is PeterKottas.DotNetCore.WindowsService. To install it using the Visual Studio Package Manager Console, just run

Install-Package PeterKottas.DotNetCore.WindowsService

There are good notes on how to get started, too.

Share:
42,041
DGaspar
Author by

DGaspar

Some say that I'm a programmer, but I just consider myself a lazy person that got into the perfect profession and I'll explain why. Lazy people usually try their hardest to find the easiest and most efficient solutions for a problem, pretty much what I do in a daily basis, with that in mind... It's with prow that I say "I'm just being paid to be lazy".

Updated on July 09, 2022

Comments

  • DGaspar
    DGaspar almost 2 years

    I'm trying to build a Windows Service in .Net Core 2.0 but I've been banging my head on the wall for a full day and no progress at all. Everything seems to be using Core 1.0/1.1 even the Microsoft documentation:

    Host an ASP.NET Core app in a Windows Service

    TopShelf doesn't support 2.0 as well, for what I've seen.

    I've seen some weird solutions that put all the code in a .Net Standard Class Library and then use a .Net Framework application to host the Windows Service, but this doesn't look elegant in my eyes and I'm trying to get rid of.Net Framework altogether.

    Is what I want to do even possible at the moment? Am I missing something really basic?

  • Jeroen Mostert
    Jeroen Mostert about 6 years
    I deleted my comment. I also wrote up a new answer to point out that there's now an MS-based solution for services in .NET Core 2.0 (although using some third-party solution is still more convenient). I trust that soothes any harshness. :-)
  • DGaspar
    DGaspar about 6 years
    I currently have my service running with github.com/dasMulli/dotnet-win32-service but when I can I'll try and fiddle with this solution, since it looks like the best for future proofing, and ofc it looks like it'll be the native way of doing it. If all goes well I'll accept your answer.
  • DGaspar
    DGaspar about 6 years
    In my case I don't want it to host a WebSite, I just want it to run some background tasks like zipping some files and upload them via FTP and some other stuff, would you recon this would work as well?
  • Weicheng
    Weicheng almost 6 years
    @DGaspar For your requirement, I would try creating a .Net Core Console App, and use nssm to host the Console App.
  • DGaspar
    DGaspar over 5 years
    As I mentioned previously, PeterKottas's nugget uses DasMuli's nugget code internally. I'm currently using DasMuli's one, but PeterKottas's should also work.
  • Panagiotis Kanavos
    Panagiotis Kanavos over 5 years
    That's what the top answer already explains. Link-only answers that don't actually explain how the problem is solved aren't very helpful
  • Panagiotis Kanavos
    Panagiotis Kanavos over 5 years
    It doesn't point out anything. It's only a few calls to sc.exe without any explanation. That github repo can disappear at any time which would make this answer unusable. As it is, someone has to already know about services and sc.exe just to understand what that repo contains
  • Panagiotis Kanavos
    Panagiotis Kanavos over 5 years
    Besides, Jeroen's answer explains the important steps that are missing from this repo - configuring the event source.
  • Dima Kozhevin
    Dima Kozhevin over 5 years
    A link to a solution is welcome, but please ensure your answer is useful without it: add context around the link so your fellow users will have some idea what it is and why it’s there, then quote the most relevant part of the page you're linking to in case the target page is unavailable. Answers that are little more than a link may be deleted.
  • Justin
    Justin about 5 years
    With this approach, what type of config file is supported? App.config or Appsettings.json?
  • Jeroen Mostert
    Jeroen Mostert about 5 years
    @Justin: there is nothing specific in the service code that mandates either. You can use whatever a regular .NET Core console application would support (which depends on the packages you add and the code you use).
  • CodingYourLife
    CodingYourLife about 5 years
    At first glance this seems to work perfectly. notice that you need to create a .NET Core CONSOLE application and add the package. You can then install that console application to run like a service. I've overlooked that few times before I got it running. So it's NOT for a .NET Windows Servce
  • andrew pate
    andrew pate almost 5 years
    Works for me. Although I had to give the full path to the .exe in binPath, probably because I have code finding the pathtoContentRoot.
  • andrew pate
    andrew pate almost 5 years
    Note: If you use a configuration file such as appsettings.json, you may need to do something like this to get it... _jsonConfigurationFile = Path.Combine(pathToContentRoot, jsonConfigurationFile)