web-dev-qa-db-fra.com

Application console .NET en tant que service Windows

J'ai une application console et j'aimerais l'exécuter en tant que service Windows. VS2010 a un modèle de projet qui permet de joindre un projet de console et de créer un service Windows. Je souhaite ne pas ajouter de projet de service séparé et, si possible, intégrer le code de service dans une application console afin de conserver l'application console en tant que projet pouvant être exécuté en tant qu'application console ou en tant que service Windows s'il est exécuté par exemple à partir d'une ligne de commande à l'aide de commutateurs.

Quelqu'un pourrait peut-être suggérer une bibliothèque de classe ou un extrait de code qui pourrait rapidement et facilement transformer une application de la console c # en service?

133
Tomas

J'utilise généralement le techinque suivant pour exécuter la même application en tant qu'application console ou en tant que service:

public static class Program
{
    #region Nested classes to support running as service
    public const string ServiceName = "MyService";

    public class Service : ServiceBase
    {
        public Service()
        {
            ServiceName = Program.ServiceName;
        }

        protected override void OnStart(string[] args)
        {
            Program.Start(args);
        }

        protected override void OnStop()
        {
            Program.Stop();
        }
    }
    #endregion

    static void Main(string[] args)
    {
        if (!Environment.UserInteractive)
            // running as service
            using (var service = new Service())
                ServiceBase.Run(service);
        else
        {
            // running as console app
            Start(args);

            Console.WriteLine("Press any key to stop...");
            Console.ReadKey(true);

            Stop();
        }
    }

    private static void Start(string[] args)
    {
        // onstart code here
    }

    private static void Stop()
    {
        // onstop code here
    }
}

Environment.UserInteractive est normalement vrai pour l'application de la console et faux pour un service. Sur le plan technique, il est possible d'exécuter un service en mode interactif avec l'utilisateur. Vous pouvez donc vérifier un commutateur de ligne de commande.

170
VladV

J'ai eu beaucoup de succès avec TopShelf .

TopShelf est un package Nuget conçu pour faciliter la création d'applications Windows .NET pouvant être exécutées en tant qu'applications de console ou en tant que services Windows. Vous pouvez rapidement connecter des événements tels que les événements de démarrage et d'arrêt de votre service, configurer à l'aide de code, par exemple. pour définir le compte sous lequel il est exécuté, configurez les dépendances sur d'autres services et configurez le mode de récupération des erreurs.

À partir de la console du gestionnaire de packages (Nuget):

Topshelf de paquet d'installation

Reportez-vous à exemples de code pour commencer.

Exemple:

HostFactory.Run(x =>                                 
{
    x.Service<TownCrier>(s =>                        
    {
       s.ConstructUsing(name=> new TownCrier());     
       s.WhenStarted(tc => tc.Start());              
       s.WhenStopped(tc => tc.Stop());               
    });
    x.RunAsLocalSystem();                            

    x.SetDescription("Sample Topshelf Host");        
    x.SetDisplayName("Stuff");                       
    x.SetServiceName("stuff");                       
}); 

TopShelf prend également en charge l’installation du service, ce qui permet de gagner beaucoup de temps et supprime le code standard de votre solution. Pour installer votre fichier .exe en tant que service, vous devez exécuter la commande suivante à partir de la commande Invite:

myservice.exe install -servicename "MyService" -displayname "My Service" -description "This is my service."

Vous n'avez pas besoin de connecter un ServiceInstaller et tout cela - TopShelf le fait pour vous.

50
saille

Alors voici la procédure complète:

  1. Créer un nouveau projet d'application console (par exemple, MyService)
  2. Ajoutez deux références de bibliothèque: System.ServiceProcess et System.Configuration.Install
  3. Ajoutez les trois fichiers imprimés ci-dessous
  4. Générez le projet et exécutez "InstallUtil.exe c:\chemin\to\MyService.exe"
  5. Maintenant, vous devriez voir MyService sur la liste de services (exécutez services.msc)

* InstallUtil.exe peut généralement être trouvé ici: C:\windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.ex‌ e

Program.cs

using System;
using System.IO;
using System.ServiceProcess;

namespace MyService
{
    class Program
    {
        public const string ServiceName = "MyService";

        static void Main(string[] args)
        {
            if (Environment.UserInteractive)
            {
                // running as console app
                Start(args);

                Console.WriteLine("Press any key to stop...");
                Console.ReadKey(true);

                Stop();
            }
            else
            {
                // running as service
                using (var service = new Service())
                {
                    ServiceBase.Run(service);
                }
            }
        }

        public static void Start(string[] args)
        {
            File.AppendAllText(@"c:\temp\MyService.txt", String.Format("{0} started{1}", DateTime.Now, Environment.NewLine));
        }

        public static void Stop()
        {
            File.AppendAllText(@"c:\temp\MyService.txt", String.Format("{0} stopped{1}", DateTime.Now, Environment.NewLine));
        }
    }
}

MyService.cs

using System.ServiceProcess;

namespace MyService
{
    class Service : ServiceBase
    {
        public Service()
        {
            ServiceName = Program.ServiceName;
        }

        protected override void OnStart(string[] args)
        {
            Program.Start(args);
        }

        protected override void OnStop()
        {
            Program.Stop();
        }
    }
}

MyServiceInstaller.cs

using System.ComponentModel;
using System.Configuration.Install;
using System.ServiceProcess;

namespace MyService
{
    [RunInstaller(true)]
    public class MyServiceInstaller : Installer
    {
        public MyServiceInstaller()
        {
            var spi = new ServiceProcessInstaller();
            var si = new ServiceInstaller();

            spi.Account = ServiceAccount.LocalSystem;
            spi.Username = null;
            spi.Password = null;

            si.DisplayName = Program.ServiceName;
            si.ServiceName = Program.ServiceName;
            si.StartType = ServiceStartMode.Automatic;

            Installers.Add(spi);
            Installers.Add(si);
        }
    }
}
23
Nikolai Koudelia

Vous pouvez utiliser

reg add HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run /v ServiceName /d "c:\path\to\service\file\exe"

Et il apparaîtra dans la liste de services. Je ne sais pas si cela fonctionne correctement. Un service doit généralement écouter plusieurs événements.

Il existe cependant plusieurs encapsuleurs de services pouvant exécuter n'importe quelle application en tant que service réel. Par exemple, Microsofts SrvAny à partir du Kit de ressources Win20

2
Darcara

Premièrement, j'intègre la solution d'application de console dans la solution de service Windows et la référence.

Puis je rends l'application console classe de programme publique

/// <summary>
/// Hybrid service/console application
/// </summary>
public class Program
{
}

Je crée ensuite deux fonctions dans l'application console

    /// <summary>
    /// Used to start as a service
    /// </summary>
    public void Start()
    {
        Main();
    }

    /// <summary>
    /// Used to stop the service
    /// </summary>
    public void Stop()
    {
       if (Application.MessageLoop)
            Application.Exit();   //windows app
        else
            Environment.Exit(1);  //console app
    }

Ensuite, dans le service Windows lui-même, j'instancie le programme et appelle les fonctions Démarrer et Arrêter ajoutées dans OnStart et OnStop. Voir ci-dessous

class WinService : ServiceBase
{
    readonly Program _application = new Program();

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    static void Main()
    {
        ServiceBase[] servicesToRun = { new WinService() };
        Run(servicesToRun);
    }

    /// <summary>
    /// Set things in motion so your service can do its work.
    /// </summary>
    protected override void OnStart(string[] args)
    {
        Thread thread = new Thread(() => _application.Start());
        thread.Start();
    }

    /// <summary>
    /// Stop this service.
    /// </summary>
    protected override void OnStop()
    {
        Thread thread = new Thread(() => _application.Stop());
        thread.Start();
    }
}

Cette approche peut également être utilisée pour une application Windows/service Windows hybride.

2
Patrick Reynolds

J'entends votre propos en voulant qu'un assemblage arrête le code répété, mais cela serait plus simple, réduirait la répétition du code et faciliterait la réutilisation de votre code à l’avenir si vous le divisiez en ...... si vous le divisiez en 3 assemblys.

  1. Une bibliothèque bibliothèque qui fait tout le travail. Ensuite, avoir deux projets très très minces/simples:
  2. celui qui est la ligne de commande
  3. celui qui est le service windows.
2
JonAlb

Peut-être devriez-vous définir ce dont vous avez besoin. Autant que je sache, vous ne pouvez pas exécuter votre application en tant que console ou service avec une ligne de commande simultanément. N'oubliez pas que le service est installé et que vous devez le démarrer dans le Gestionnaire de services. Vous pouvez créer une nouvelle application qui lance le service ou lance un nouveau processus exécutant votre application de console. Mais comme tu l'as écrit

"garder l'application console comme un projet"

Une fois, j'étais à votre place, transformant une application console en service. Vous avez d’abord besoin du modèle si vous travaillez avec VS Express Edition. Voici un lien où vous pouvez faire vos premiers pas: Service Windows C # , cela m'a été très utile. Ensuite, en utilisant ce modèle, ajoutez votre code aux événements souhaités du service.

Pour améliorer votre service, vous pouvez faire autre chose, mais ce n’est pas rapide et/ou facile: utiliser appdomains et créer des dll pour charger/décharger. Dans l'une, vous pouvez démarrer un nouveau processus avec l'application console et dans une autre DLL, vous pouvez simplement mettre la fonctionnalité que le service doit faire.

Bonne chance.

0
BlackCath

Vous devez séparer les fonctionnalités dans une ou plusieurs classes et les lancer via l’un des deux stubs. Le talon de la console ou du service.

Comme on peut le voir, lors de l’exécution de Windows, la myriade de services qui composent l’infrastructure ne présente pas (et ne peut pas directement) présenter les fenêtres de la console à l’utilisateur. Le service doit communiquer avec l'utilisateur de manière non graphique: via le SCM; dans le journal des événements, dans un fichier journal, etc. Le service devra également communiquer avec Windows via le SCM, sinon il sera arrêté.

Il serait évidemment acceptable de disposer d'une application de console capable de communiquer avec le service, mais celui-ci doit s'exécuter de manière indépendante, sans nécessiter d'interaction avec l'interface graphique.

Le talon de la console peut être très utile pour le comportement du service de débogage, mais ne doit pas être utilisé dans un environnement "production" qui, après tout, sert à la création d'un service.

Je ne l'ai pas lu complètement mais cet article semble aller dans la bonne direction.

0
Jodrell

J'utilise une classe de service qui suit le modèle standard prescrit par ServiceBase, et j'ajoute des aides au débogage F5 facile. Cela permet de définir les données de service au sein du service, de les retrouver facilement et de gérer leur durée de vie.

Je crée normalement une application Windows avec la structure ci-dessous. Je ne crée pas d'application console. De cette façon, je ne reçois pas une grosse boîte noire à chaque fois que je lance l'application. Je reste dans le débogueur où toute l'action est. J'utilise Debug.WriteLine pour que les messages aillent dans la fenêtre de sortie qui s’ancre bien et reste visible après la fin de l’application.

D'habitude, je ne me soucie pas d'ajouter du code de débogage pour arrêter; Je viens d'utiliser le débogueur à la place. Si j’ai besoin de déboguer pour arrêter, je fais du projet une application console, j’ajoute une méthode Stop forwarder et l’appelle après un appel à Console.ReadKey.

public class Service : ServiceBase
{
    protected override void OnStart(string[] args)
    {
        // Start logic here.
    }

    protected override void OnStop()
    {
        // Stop logic here.
    }

    static void Main(string[] args)
    {
        using (var service = new Service()) {
            if (Environment.UserInteractive) {
                service.Start();
                Thread.Sleep(Timeout.Infinite);
            } else
                Run(service);
        }
    }
    public void Start() => OnStart(null);
}
0
Edward Brey