web-dev-qa-db-fra.com

Service Windows .Net Core 2.0

J'essaie de créer un service Windows dans .Net Core 2.0, mais je me suis cogné la tête contre le mur pendant une journée complète et aucun progrès. Tout semble utiliser Core 1.0/1.1, même la documentation Microsoft:

Héberger une application ASP.NET Core dans un service Windows

TopShelf ne supporte pas la version 2.0 également, pour ce que j'ai vu.

J'ai vu des solutions étranges qui mettent tout le code dans une bibliothèque de classes .Net Standard, puis utilisent une application .Net Framework pour héberger le service Windows, mais cela n'a pas l'air élégant à mes yeux et j'essaie d'obtenir débarrasser de.Net Framework tout à fait.

Est-ce que ce que je veux faire est même possible pour le moment? Est-ce que je manque quelque chose de vraiment basique?

46
DGaspar

Il est maintenant possible d'écrire un service Windows dans .NET Core 2.0 sans bibliothèques tierces, grâce à la sortie du pack de compatibilité Windows (à la moment de l'écriture, toujours en pré-sortie). Comme le signale la page elle-même:

Mais avant de commencer le portage, vous devez comprendre ce que vous voulez accomplir avec la migration. Le portage sur .NET Core, car il s'agit d'une nouvelle implémentation .NET, n'est pas une raison suffisante (sauf si vous êtes un vrai fan).

En particulier, l'écriture d'un service Windows dans .NET Core peut désormais être possible, mais vous n'obtiendrez pas immédiatement une compatibilité entre plates-formes, car les assemblys des plates-formes autres que Windows émettront simplement un PlatformNotSupportedException si vous tentez utiliser le code de service. Il est possible de contourner ce problème (en utilisant RuntimeInformation.IsOSPlatform, par exemple), mais c'est une autre question.

De plus, les bibliothèques tierces peuvent toujours offrir une interface plus agréable pour l’installation du service: au moment de l’écriture, la version actuelle du pack de compatibilité (2.0.0-preview1-26216-02) ne prend pas en charge l’espace de noms System.Configuration.Install, L'approche par défaut avec ServiceProcessInstaller class et installutil ne fonctionnera pas. Plus sur cela plus tard.

Cela dit, supposons que vous ayez créé un tout nouveau service Windows (Service1) à partir du modèle de projet (non strictement nécessaire, car il ne contient rien d’intéressant, à part une classe héritant de ServiceBase). Tout ce que vous avez à faire pour le construire sur .NET Core 2.0 est d’éditer et de remplacer le .csproj par le nouveau 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>

Et ensuite, supprimez properties\AssemblyInfo.cs car ce n'est plus nécessaire et sera en conflit avec les informations de version contenues dans le projet lui-même.

Si vous avez déjà un service et qu'il comporte des dépendances, la conversion peut être plus compliquée. Voir ici .

Vous devriez maintenant pouvoir exécuter dotnet publish et obtenir un exécutable. Comme mentionné, vous ne pouvez pas utiliser la classe ServiceProcessInstaller pour installer le service, vous devez donc manuellement

  • enregistrer la source d'événement utilisée par le service;
  • créer le service réel.

Cela peut être fait avec certains PowerShell. A partir d'une invite élevée à l'emplacement contenant votre exécutable publié:

$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)

Cela n’est pas idéal à plusieurs égards: cela code en dur le chemin du fichier de ressource de message (nous devrions vraiment déterminer son emplacement entre l’exécutable et les chemins d’exécution du registre) et en codant en dur le nom du service et l’exécutable. Nom. Vous voudrez peut-être doter votre projet de ses propres capacités d'installation en procédant à une analyse de la ligne de commande dans Program.cs, ou en utilisant l'une des bibliothèques mentionnées dans La réponse de Cocowalla .

26
Jeroen Mostert

Pour héberger l'API Web .NET Core 2.0 en tant que service Windows. J'ai suivi ce guide hôte ASP.NET Core dans un service Windows . Les prérequis ne me sont pas clairs. Après quelques erreurs, voici ce que j'ai fait: Code source

  1. Créer une application Web ASP.NET Core enter image description here
  2. Choisissez l'API enter image description here
  3. Éditez le fichier .csproj, vous devez changer le cadre cible de netcoreapp2.0 à net461 , liste explicitement toutes les références de package plutôt que d'utiliser Microsoft.AspNetCore.All , comme suit
<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 [dossier-solution] dotnet publish -o "[dossier-publier]"
  2. power Shell [solution-dossier] sc.exe crée CoreApi binpath = "[publication-dossier]\CoreApiHostedAsWindowsService.exe"
  3. power Shell [solution-folder] sc.exe démarrer CoreApi
  4. visite par défaut api power Shell [dossier-solution] Invoke-WebRequest http: // localhost: 5000/api/values
14
Chenger

Je vais résumer quelques options:

  1. Déplacez votre code dans une bibliothèque .NET Standard et hébergez-le dans une application .NET Framework afin de pouvoir utiliser ServiceBase. Bien entendu, cela nécessitera que le .NET Framework soit installé sur la machine cible.
  2. Utilisez NSSM (le gestionnaire de services non-succion) pour gérer une application de console .NET Core (elle possède une licence de domaine public)
  3. Utilisez les appels de l'API Windows pour vous connecter aux méthodes de service Windows. C'est l'approche adoptée par DotNetCore.WindowsService et dotnet-win32-service (les deux sont sous licence MIT

Je pense que le commentaire de @ JeroenMostert est un peu dur - je peux voir l'intérêt de ne pas dépendre d'une version .NET Framework particulière disponible sur les machines cibles. Bien d’autres ressentent manifestement la même chose, car les deux pensions auxquelles j’ai lié sont plutôt populaires.

12
Cocowalla

Dans .NET Core 2.1, vous pouvez utiliser Host et HostBuilder pour obtenir une application console qui s'exécute en tant que service. Si vous conteneurisez votre application console, vous pouvez déployer le conteneur n'importe où, ce qui revient à exécuter en tant que service. Vous êtes en mesure d'utiliser Host et HostBuilder pour gérer les DI, la journalisation, la fermeture progressive, etc. dans votre application de console. Jettes un coup d'oeil à:

Hébergement de services dans une application console .NET Core

8
Peter

Un moyen simple de créer un service Windows .NET Core consiste à utiliser la bibliothèque de la bibliothèque DotNetCore.WindowsService de Peter Kottas .

Le package NuGet est PeterKottas.DotNetCore.WindowsService . Pour l'installer à l'aide de la console Visual Studio Package Manager, exécutez simplement

Install-Package PeterKottas.DotNetCore.WindowsService

Il y a bonnes notes sur la façon de commencer , aussi.

4
Josh Withee

Nous avons simplement besoin du package System.ServiceProcess.ServiceController NuGet pour exécuter une application .NET Core en tant que service Windows.

Voici le fichier .csproj,

<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>

Fichier Program.cs,

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
    }
}

Construisez et publiez la solution.

  1. Ouvrez l'invite Cmd en mode Admin à partir du dossier .exe. Exemple:\WindowsService101\bin\Debug\netcoreapp2.1\publish

  2. sc créer binPath = ""

  3. début de sc

2
DeviZ

Il s’agit peut-être d’une échappatoire complète, mais rappelez-vous qu’avec un plus grand support de docker, vous pourrez peut-être créer un service fonctionnant dans un conteneur. À ce stade, il s'agirait toujours d'un noyau .net (2.0) mais fonctionnant sous Windows. De plus, vous pouvez déployer presque n'importe où dans le futur.

À mesure que le noyau dotnet mûrit, cette solution est de mieux en mieux, en supposant que votre service ne nécessite pas de ressources locales de l'hôte.

2
Killnine

ASP.NET Core dans un service Windows pour .NET Core 2.2. Apportez les modifications suivantes à un projet ASP.NET Core existant pour exécuter l'application en tant que service:

Nécessite: PowerShell 6.2 or later

Déploiement dépendant de la structure (FDD):

Le déploiement dépendant de l'infrastructure (FDD) repose sur la présence d'une version partagée de l'ensemble de systèmes .NET Core sur le système cible. Lorsque le scénario FDD est utilisé avec une application de service Windows ASP.NET Core, le SDK génère un exécutable (* .exe), appelé exécutable dépendant de la structure.

Ajoutez un Windows identifiant d'exécution (RID) au <PropertyGroup> qui contient le framework cible. Dans l'exemple suivant, le RID est défini sur win7-x64. Ajoutez la propriété <SelfContained> à la valeur false. Ces propriétés indiquent au SDK de générer un fichier exécutable (.exe) pour Windows.

Un fichier web.config, qui est normalement généré lors de la publication d'une application ASP.NET Core, n'est pas nécessaire pour une application Windows Services. Pour désactiver la création du fichier web.config, ajoutez la propriété <IsTransformWebConfigDisabled> définie sur true.

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

Déploiement autonome (SCD):

Le déploiement autonome (SCD) ne repose pas sur la présence de composants partagés sur le système cible. Les dépendances de l'exécution et de l'application sont déployées avec l'application sur le système d'hébergement.

Confirmez la présence d'un identifiant Windows Runtime (RID) ou ajoutez-en un au <PropertyGroup> qui contient le cadre cible. Désactivez la création d'un fichier web.config en ajoutant la propriété <IsTransformWebConfigDisabled> définie sur 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>();
}

Publier un déploiement dépendant de la structure (FDD):

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

Publier un déploiement autonome (SCD)

Le RID doit être spécifié dans la propriété <RuntimeIdenfifier> (ou <RuntimeIdentifiers>) du fichier de projet. Fournissez le temps d'exécution à l'option - r | --runtime de la commande dotnet publish.

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

Accordez un accès en écriture/lecture/exécution au dossier de l'application à l'aide de la commande icacls via un shell d'administration de commande PowerShell 6.

icacls "{PATH}" /grant "{USER ACCOUNT}:(OI)(CI){PERMISSION FLAGS}" /t
  • {PATH} - Chemin d'accès au dossier de l'application.
  • {COMPTE UTILISATEUR} - Le compte d'utilisateur (SID).
  • (OI) - L'indicateur Object Inherit propage les autorisations sur les fichiers subordonnés.
  • (CI) - L’indicateur Container Inherit propage les autorisations aux dossiers subordonnés.
  • {PERMISSION FLAGS} - Définit les autorisations d'accès de l'application.
    • Écrire (W)
    • Lire (R)
    • Exécuter (X)
    • Complet (F)
    • Modifier (M)
  • / t - Applique de manière récursive aux dossiers et fichiers subordonnés existants.

Commander:

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

Utilisez le script RegisterService.ps1 PowerShell pour enregistrer le service. A partir d'un shell administratif de commande PowerShell 6, exécutez le script à l'aide de la commande suivante:

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

Démarrez le service avec la commande Start-Service -Name {NAME} PowerShell 6.

Start-Service -Name MyService

Gestion des événements de démarrage et d'arrêt

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();
    }
}

Méthode d'extension:

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

Programme.Main:

Host.RunAsCustomService();

Définissez le chemin d'accès racine du contenu au dossier de l'application:

Programme.Main:

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

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

La 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

2
Ogglas

Comme Microsoft a publié Microsoft.Windows.Compatibility, je l’utiliserais, car il me semble que c’est mieux pour une utilisation future.

Voici un exemple simple de service d’installation automatique https://github.com/janantos/service_core

0
Jan Antoš