100 votes

Installation de plusieurs instances du même service Windows sur un serveur

Nous avons donc créé un service Windows pour fournir des données à notre application client et tout se passe bien. Le client a présenté une demande de configuration amusante qui nécessite deux instances de ce service fonctionnant sur le même serveur et configurées pour pointer vers des bases de données distinctes.

Jusqu'à présent, je n'ai pas réussi à faire en sorte que cela se produise et j'espérais que mes collègues de stackoverflow pourraient me donner quelques indications sur la raison de ce phénomène.

Configuration actuelle :

J'ai configuré le projet qui contient le service Windows, que nous appellerons AppService à partir de maintenant, et le fichier ProjectInstaller.cs qui gère les étapes d'installation personnalisées pour définir le nom du service en fonction d'une clé dans App.config comme suit :

this.serviceInstaller1.ServiceName = Util.ServiceName;
this.serviceInstaller1.DisplayName = Util.ServiceName;
this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;

Dans ce cas, Util est juste une classe statique qui charge le nom du service à partir du fichier de configuration.

A partir de là, j'ai essayé deux manières différentes d'installer les deux services et les deux ont échoué de manière identique.

La première méthode consistait à installer simplement la première copie du service, à copier le répertoire installé et à le renommer, puis à exécuter la commande suivante après avoir modifié l'app config pour changer le nom du service souhaité :

InstallUtil.exe /i AppService.exe

Comme cela ne fonctionnait pas, j'ai essayé de créer un deuxième projet d'installation, j'ai modifié le fichier de configuration et j'ai construit le deuxième installateur. Lorsque j'ai exécuté le programme d'installation, tout a bien fonctionné mais le service n'apparaissait pas dans services.msc. J'ai donc exécuté la commande précédente sur la deuxième base de code installée.

Les deux fois, j'ai reçu le résultat suivant de InstallUtil (parties pertinentes seulement) :

Exécution d'une installation transactionnelle.

Début de la phase d'installation de l'installation.

Installation de Service App Service Two... Le service App Service Two a été installé avec succès. Création de la source EventLog App Service Two dans le journal Application...

Une exception s'est produite pendant la phase d'installation. System.NullReferenceException : La référence de l'objet n'est pas définie comme une instance d'un objet.

La phase de Rollback de l'installation commence.

Restauration du journal des événements à l'état précédent pour la source App Service Two. Service App Service Two est en cours de suppression du système... Service App Service Two a été supprimé avec succès du système.

La phase de Rollback s'est terminée avec succès.

L'installation transactionnelle est terminée. L'installation a échoué et le retour en arrière a été effectué.

Désolé pour ce long post, je voulais m'assurer qu'il y avait suffisamment d'informations pertinentes. L'élément qui me laisse perplexe jusqu'à présent est le fait que l'installation du service se termine avec succès et que ce n'est qu'après avoir créé la source du journal d'événements que l'exception NullReferenceException semble être levée. Si quelqu'un sait ce que je fais de travers ou a une meilleure approche, il serait très apprécié.

84voto

jamesaharvey Points 5820

Avez-vous essayé le sc / service controller util ? Type

sc create

à une ligne de commande, et il vous donnera l'entrée d'aide. Je pense que j'ai fait cela dans le passé pour Subversion et utilisé cet article comme référence :

http://svn.apache.org/repos/asf/subversion/trunk/notes/Windows-service.txt

5 votes

J'ai trouvé cette page utile : http://journalofasoftwaredev.wordpress.com/2008/07/16/multip‌​le-instances-of-same‌​-windows-service/ . Vous pouvez insérer du code dans l'installateur pour obtenir le nom du service que vous voulez lorsque vous exécutez installutil.

9 votes

Le lien vers le blog wordpress a été modifié : journalofasoftwaredev.wordpress.com/2008/07

21voto

Rajesh Kumar Points 9
  sc create [servicename] binpath= [path to your exe]

Cette solution a fonctionné pour moi.

6 votes

Juste pour signaler ; [path to your exe] doit être le chemin complet et n'oubliez pas l'espace après binpath=

2 votes

Cela permet en effet d'installer un service plusieurs fois. Cependant, toutes les informations fournies par l'installateur du service. Par exemple, la description, le type de connexion, etc. sont ignorées.

20voto

Mark Redman Points 10816

Vous pouvez exécuter plusieurs versions d'un même service en procédant comme suit :

1) Copiez l'exécutable et la configuration du service dans son propre dossier.

2) Copiez Install.Exe dans le dossier de l'exécutable du service (à partir du dossier .net framework).

3) Créez un fichier de configuration appelé Install.exe.config dans le dossier de l'exécutable du service avec le contenu suivant (noms de service uniques) :

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="ServiceName" value="The Service Name"/>
    <add key="DisplayName" value="The Service Display Name"/>
  </appSettings>
</configuration>

4) Créez un fichier batch pour installer le service avec le contenu suivant :

REM Install
InstallUtil.exe YourService.exe
pause

5) Pendant que vous êtes là, créez un fichier de désinstallation par lots.

REM Uninstall
InstallUtil.exe -u YourService.exe
pause

EDITAR:

Note : si je n'ai pas oublié quelque chose, voici la classe ServiceInstaller (à ajuster selon les besoins) :

using System.Configuration;

namespace Made4Print
{
    partial class ServiceInstaller
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;
        private System.ServiceProcess.ServiceInstaller FileProcessingServiceInstaller;
        private System.ServiceProcess.ServiceProcessInstaller FileProcessingServiceProcessInstaller;

        /// <summary> 
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Component Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.FileProcessingServiceInstaller = new System.ServiceProcess.ServiceInstaller();
            this.FileProcessingServiceProcessInstaller = new System.ServiceProcess.ServiceProcessInstaller();
            // 
            // FileProcessingServiceInstaller
            // 
            this.FileProcessingServiceInstaller.ServiceName = ServiceName;
            this.FileProcessingServiceInstaller.DisplayName = DisplayName;
            // 
            // FileProcessingServiceProcessInstaller
            // 
            this.FileProcessingServiceProcessInstaller.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
            this.FileProcessingServiceProcessInstaller.Password = null;
            this.FileProcessingServiceProcessInstaller.Username = null;
            // 
            // ServiceInstaller
            // 
            this.Installers.AddRange(new System.Configuration.Install.Installer[] { this.FileProcessingServiceInstaller, this.FileProcessingServiceProcessInstaller });
        }

        #endregion

        private string ServiceName
        {
            get
            {
                return (ConfigurationManager.AppSettings["ServiceName"] == null ? "Made4PrintFileProcessingService" : ConfigurationManager.AppSettings["ServiceName"].ToString());
            }
        }

        private string DisplayName
        {
            get
            {
                return (ConfigurationManager.AppSettings["DisplayName"] == null ? "Made4Print File Processing Service" : ConfigurationManager.AppSettings["DisplayName"].ToString());
            }
        }
    }
}

0 votes

Je pense que ce que vous décrivez est plus ou moins ce que j'ai fait en autorisant le ServiceName et le DisplayName à être définis à partir de mes services app.config. J'ai essayé de faire ce que vous décrivez mais malheureusement cela a abouti au même problème que celui mentionné dans ma question.

0 votes

J'ai en quelque sorte un modèle que j'utilise depuis des années, alors peut-être ai-je manqué quelque chose, à quoi ressemble votre classe ServiceInstaller, je vais poster une copie de travail de celle que j'utilise, faites-moi savoir si cela vous aide ?

0 votes

Nos installateurs de services sont en fait presque identiques. J'utilise une classe statique pour charger le service et afficher les noms à partir du fichier de configuration, mais à part cela, ils sont très similaires. Je pense que la raison pour laquelle cela ne fonctionne pas pour moi est qu'il y a peut-être quelque chose d'un peu particulier dans notre code de service. Malheureusement, beaucoup de personnes s'en sont occupées. D'après ce que je comprends, votre réponse devrait fonctionner dans la majorité des cas, merci pour votre aide.

11voto

Jonathon Watney Points 2318

Vieille question, je sais, mais j'ai eu de la chance en utilisant l'option /servicename sur InstallUtil.exe. Je ne la vois pas dans l'aide intégrée cependant.

InstallUtil.exe /servicename="My Service" MyService.exe

Je ne sais pas exactement où j'ai lu cela pour la première fois, mais je ne l'ai pas revu depuis. A VOIR.

3 votes

Renvoie cette erreur : An exception occurred during the Install phase. System.ComponentModel.Win32Exception: The specified service already exists

0 votes

@mkb Avez-vous un autre service appelé "Mon service" ?

0 votes

Oui, comme dans la question j'ai un service, même exécutable, mais je veux installer deux instances de celui-ci, chacune avec une configuration différente. J'ai copié-collé l'exe du service mais celui-ci n'a pas fonctionné.

4voto

chris.house.00 Points 1652

Ce que j'ai fait pour que cela fonctionne est de stocker le nom du service et le nom d'affichage dans un app.config pour mon service. Ensuite, dans ma classe d'installation, je charge l'app.config comme un XmlDocument et j'utilise xpath pour extraire les valeurs et les appliquer à ServiceInstaller.ServiceName et ServiceInstaller.DisplayName, avant d'appeler InitializeComponent(). Cela suppose que vous ne définissez pas déjà ces propriétés dans InitializeComponent(), auquel cas, les paramètres de votre fichier de configuration seront ignorés. Le code suivant est ce que j'appelle depuis le constructeur de ma classe d'installateur, avant InitializeComponent() :

       private void SetServiceName()
       {
          string configurationFilePath = Path.ChangeExtension(Assembly.GetExecutingAssembly().Location, "exe.config");
          XmlDocument doc = new XmlDocument();
          doc.Load(configurationFilePath);

          XmlNode serviceName = doc.SelectSingleNode("/xpath/to/your/@serviceName");
          XmlNode displayName = doc.SelectSingleNode("/xpath/to/your/@displayName");

          if (serviceName != null && !string.IsNullOrEmpty(serviceName.Value))
          {
              this.serviceInstaller.ServiceName = serviceName.Value;
          }

          if (displayName != null && !string.IsNullOrEmpty(displayName.Value))
          {
              this.serviceInstaller.DisplayName = displayName.Value;
          }
      }

Je ne pense pas que la lecture du fichier de configuration directement à partir de ConfigurationManager.AppSettings ou quelque chose de similaire fonctionnera car lorsque le programme d'installation s'exécute, il est exécuté dans le contexte de InstallUtil.exe, et non du .exe de votre service. Vous pouvez peut-être faire quelque chose avec ConfigurationManager.OpenExeConfiguration, mais dans mon cas, cela n'a pas fonctionné car j'essayais d'accéder à une section de configuration personnalisée qui n'était pas chargée.

0 votes

Bonjour Chris House ! Je suis tombé sur votre réponse parce que je suis en train de construire une API Web auto-hébergée basée sur OWIN autour du planificateur Quartz.NET et de la placer dans un service Windows. Plutôt malin ! J'espère que vous allez bien !

0 votes

Bonjour Chris House ! Je suis tombé sur votre réponse parce que je suis en train de construire une API Web auto-hébergée basée sur OWIN autour du planificateur Quartz.NET et de la placer dans un service Windows. Plutôt malin ! J'espère que vous allez bien !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X