简体   繁体   中英

.Net Core 2.0 Windows Service

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?

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 .

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 在此处输入图片说明
  2. Choose API 在此处输入图片说明
  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

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.

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

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.

Maybe this is a complete cop-out, but remember that with greater docker support, you may be able to build out a service that runs within a container. At that point, it would still be .net core (2.0) but running on your windows box. What's more, you could deploy just about anywhere in the future.

As dotnet core matures, I this is a better and better solution, assuming your service doesn't require resources local to the host.

We just need System.ServiceProcess.ServiceController NuGet package to run a .NET Core application as Windows Service.

Following is the .csproj file,

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

<PropertyGroup>
  <OutputType>Exe</OutputType>
  <TargetFramework>netcoreapp2.1</TargetFramework>
  <RuntimeIdentifier>win7-x64</RuntimeIdentifier>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.ServiceProcess.ServiceController" 
 Version="4.5.0" />
</ItemGroup>

</Project>

Program.cs file,

using System.ServiceProcess;
namespace WindowsService101
{
class Program
{
    static void Main(string[] args)
    {
        using (var service = new HelloWorldService())
        {
            ServiceBase.Run(service);
        }
    }
}
}



public class HelloWorldService : ServiceBase
{
    protected override void OnStart(string[] args)
    {
       // Code Here
    }

    protected override void OnStop()
    {
        // Code Here
    }
}

Build and Publish the solution.

  1. Open Cmd Prompt in Admin mode from the .exe folder Sample: \\WindowsService101\\bin\\Debug\\netcoreapp2.1\\publish

  2. sc create binPath=""

  3. sc start

ASP.NET Core in a Windows Service for .NET Core 2.2 . Make the following changes to an existing ASP.NET Core project to run the app as a service:

Requires: PowerShell 6.2 or later

Framework-dependent Deployment (FDD):

Framework-dependent deployment (FDD) relies on the presence of a shared system-wide version of .NET Core on the target system. When the FDD scenario is used with an ASP.NET Core Windows Service app, the SDK produces an executable (*.exe), called a framework-dependent executable.

Add a Windows Runtime Identifier (RID) to the <PropertyGroup> that contains the target framework. In the following example, the RID is set to win7-x64 . Add the <SelfContained> property set to false . These properties instruct the SDK to generate an executable (.exe) file for Windows.

A web.config file, which is normally produced when publishing an ASP.NET Core app, is unnecessary for a Windows Services app. To disable the creation of the web.config file, add the <IsTransformWebConfigDisabled> property set to true .

<PropertyGroup>
  <TargetFramework>netcoreapp2.2</TargetFramework>
  <RuntimeIdentifier>win7-x64</RuntimeIdentifier>
  <SelfContained>false</SelfContained>
  <IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
</PropertyGroup>

Self-contained Deployment (SCD):

Self-contained deployment (SCD) doesn't rely on the presence of shared components on the target system. The runtime and the app's dependencies are deployed with the app to the hosting system.

Confirm the presence of a Windows Runtime Identifier (RID) or add a RID to the <PropertyGroup> that contains the target framework. Disable the creation of a web.config file by adding the <IsTransformWebConfigDisabled> property set to true .

<PropertyGroup>
  <TargetFramework>netcoreapp2.2</TargetFramework>
  <RuntimeIdentifier>win7-x64</RuntimeIdentifier>
  <IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
</PropertyGroup>

Program.Main

public class Program
{
    public static void Main(string[] args)
    {
        var isService = !(Debugger.IsAttached || args.Contains("--console"));

        if (isService)
        {
            var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
            var pathToContentRoot = Path.GetDirectoryName(pathToExe);
            Directory.SetCurrentDirectory(pathToContentRoot);
        }

        var builder = CreateWebHostBuilder(
            args.Where(arg => arg != "--console").ToArray());

        var host = builder.Build();

        if (isService)
        {
            // To run the app without the CustomWebHostService change the
            // next line to host.RunAsService();
            host.RunAsCustomService();
        }
        else
        {
            host.Run();
        }
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureLogging((hostingContext, logging) =>
            {
                logging.AddEventLog();
            })
            .ConfigureAppConfiguration((context, config) =>
            {
                // Configure the app here.
            })
            .UseStartup<Startup>();
}

Publish a Framework-dependent Deployment (FDD):

dotnet publish --configuration Release --output c:\svc

Publish a Self-contained Deployment (SCD)

The RID must be specified in the <RuntimeIdenfifier> (or <RuntimeIdentifiers> ) property of the project file. Supply the runtime to the -r|--runtime option of the dotnet publish command.

dotnet publish --configuration Release --runtime win7-x64 --output c:\svc

Grant write/read/execute access to the app's folder using the icacls command via an administrative PowerShell 6 command shell.

icacls "{PATH}" /grant "{USER ACCOUNT}:(OI)(CI){PERMISSION FLAGS}" /t
  • {PATH} – Path to the app's folder.
  • {USER ACCOUNT} – The user account (SID).
  • (OI) – The Object Inherit flag propagates permissions to subordinate files.
  • (CI) – The Container Inherit flag propagates permissions to subordinate folders.
  • {PERMISSION FLAGS} – Sets the app's access permissions.
    • Write (W)
    • Read (R)
    • Execute (X)
    • Full (F)
    • Modify (M)
  • /t – Apply recursively to existing subordinate folders and files.

Command:

icacls "c:\svc" /grant "ServiceUser:(OI)(CI)WRX" /t

Use the RegisterService.ps1 PowerShell script to register the service. From an administrative PowerShell 6 command shell, execute the script with the following command:

.\RegisterService.ps1 
    -Name MyService 
    -DisplayName "My Cool Service" 
    -Description "This is the Sample App service." 
    -Exe "c:\svc\SampleApp.exe" 
    -User Desktop-PC\ServiceUser

Start the service with the Start-Service -Name {NAME} PowerShell 6 command.

Start-Service -Name MyService

Handle starting and stopping events

internal class CustomWebHostService : WebHostService
{
    private ILogger _logger;

    public CustomWebHostService(IWebHost host) : base(host)
    {
        _logger = host.Services
            .GetRequiredService<ILogger<CustomWebHostService>>();
    }

    protected override void OnStarting(string[] args)
    {
        _logger.LogInformation("OnStarting method called.");
        base.OnStarting(args);
    }

    protected override void OnStarted()
    {
        _logger.LogInformation("OnStarted method called.");
        base.OnStarted();
    }

    protected override void OnStopping()
    {
        _logger.LogInformation("OnStopping method called.");
        base.OnStopping();
    }
}

Extension method:

public static class WebHostServiceExtensions
{
    public static void RunAsCustomService(this IWebHost host)
    {
        var webHostService = new CustomWebHostService(host);
        ServiceBase.Run(webHostService);
    }
}

Program.Main:

host.RunAsCustomService();

Set the content root path to the app's folder:

Program.Main:

var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
var pathToContentRoot = Path.GetDirectoryName(pathToExe);
Directory.SetCurrentDirectory(pathToContentRoot);

CreateWebHostBuilder(args)
    .Build()
    .RunAsService();

Source:

https://github.com/aspnet/AspNetCore.Docs/tree/master/aspnetcore/host-and-deploy/windows-service/

https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/windows-service?view=aspnetcore-2.2

As Microsoft released Microsoft.Windows.Compatibility, I would use it, as it seem to be best for future use.

Simple example of self installing service is here https://github.com/janantos/service_core

For those finding this question but want to implement a windows service with .NET Core 3.x

https://csharp.christiannagel.com/2019/10/15/windowsservice/

A combination of the generic host plus the background service plus the commandline tool sc and you have a windows service.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM