it-swarm.com.de

Installieren mehrerer Instanzen desselben Windows-Dienstes auf einem Server

Daher haben wir einen Windows-Dienst erstellt, der Daten an unsere Client-Anwendung weiterleitet, und alles läuft hervorragend. Der Client hat eine unterhaltsame Konfigurationsanforderung erstellt, für die zwei Instanzen dieses Dienstes auf demselben Server ausgeführt und so konfiguriert werden müssen, dass sie auf separate Datenbanken verweisen.

Bisher war ich nicht in der Lage, dies zu erreichen und hoffte, dass meine Stackoverflow-Kollegen in der Lage sein könnten, einige Hinweise zu geben, warum.

Aktuelles Setup:

Ich habe das Projekt eingerichtet, das den Windows-Dienst enthält, wir nennen es ab sofort AppService und die ProjectInstaller.cs-Datei, die benutzerdefinierte Installationsschritte verarbeitet, um den Dienstnamen basierend auf einem Schlüssel in der App.config wie folgt festzulegen :

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

In diesem Fall ist Util nur eine statische Klasse, die den Dienstnamen aus der Konfigurationsdatei lädt.

Von hier an habe ich zwei verschiedene Wege ausprobiert, um beide Dienste zu installieren, und beide sind auf identische Weise fehlgeschlagen.

Die erste Möglichkeit bestand darin, einfach die erste Kopie des Dienstes zu installieren, das installierte Verzeichnis zu kopieren und umzubenennen und dann nach dem Ändern der App-Konfiguration den folgenden Befehl auszuführen, um den gewünschten Dienstnamen zu ändern:

InstallUtil.exe /i AppService.exe

Als das nicht funktionierte, habe ich versucht, ein zweites Installationsprojekt zu erstellen, die Konfigurationsdatei bearbeitet und das zweite Installationsprogramm erstellt. Als ich das Installationsprogramm ausführte, funktionierte es einwandfrei, aber der Dienst wurde nicht in services.msc angezeigt, sodass ich den vorherigen Befehl für die zweite installierte Codebasis ausführte.

Beide Male habe ich die folgende Ausgabe von InstallUtil erhalten (nur relevante Teile):

Ausführen einer abgeschlossenen Installation.

Beginn der Installationsphase der Installation.

Service App Service Two wird installiert ... Service App Service Two wurde erfolgreich installiert. Erstellen des EventLog-Quell-App-Service Zwei in der Protokollanwendung ...

Während der Installationsphase ist eine Ausnahme aufgetreten. System.NullReferenceException: Der Objektverweis wurde nicht auf eine Instanz eines Objekts festgelegt.

Die Rollback-Phase der Installation beginnt.

Wiederherstellen des Ereignisprotokolls auf den vorherigen Status für den Quell-App-Service Zwei. Service App Service Two wird aus dem System entfernt ... Service App Service Two wurde erfolgreich aus dem System entfernt.

Die Rollback-Phase wurde erfolgreich abgeschlossen.

Die durchgeführte Installation ist abgeschlossen. Die Installation ist fehlgeschlagen und das Rollback wurde durchgeführt.

Entschuldigung für den langwierigen Beitrag, wollte sicherstellen, dass es genügend relevante Informationen gibt. Was mich bisher stutzig gemacht hat, ist die Aussage, dass die Installation des Dienstes erfolgreich abgeschlossen wurde und erst, nachdem die EventLog-Quelle erstellt wurde, die NullReferenceException ausgelöst zu werden scheint. Wenn also jemand weiß, was ich falsch mache oder einen besseren Ansatz hat, wäre er sehr dankbar.

93
Switters

Haben Sie den sc/service controller util ausprobiert? Art

sc create

an einer Befehlszeile, und es wird Ihnen den Hilfeeintrag geben. Ich glaube, ich habe dies in der Vergangenheit für Subversion getan und diesen Artikel als Referenz verwendet:

http://svn.Apache.org/repos/asf/Subversion/trunk/notes/windows-service.txt

81
jamesaharvey

Sie können mehrere Versionen desselben Dienstes ausführen, indem Sie folgendermaßen vorgehen:

1) Kopieren Sie die ausführbare Datei und die Konfiguration des Dienstes in einen eigenen Ordner.

2) Kopieren Sie Install.Exe in den ausführbaren Ordner des Dienstes (aus dem .net Framework-Ordner).

3) Erstellen Sie eine Konfigurationsdatei mit dem Namen Install.exe.config im ausführbaren Ordner des Dienstes mit folgendem Inhalt (eindeutige Dienstnamen):

<?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) Erstellen Sie eine Batchdatei, um den Dienst mit den folgenden Inhalten zu installieren:

REM Install
InstallUtil.exe YourService.exe
pause

5) Erstellen Sie dort eine Batch-Deinstallationsdatei

REM Uninstall
InstallUtil.exe -u YourService.exe
pause

EDIT:

Beachten Sie bitte, dass Sie die ServiceInstaller-Klasse angeben müssen, wenn Sie etwas übersehen haben:

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());
            }
        }
    }
}
19
Mark Redman
  sc create [servicename] binpath= [path to your exe]

Diese Lösung hat bei mir funktioniert.

18
Rajesh Kumar

Alte Frage, ich weiß, aber ich hatte Glück mit der Option/servicename auf InstallUtil.exe. Ich sehe es jedoch nicht in der eingebauten Hilfe.

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

Ich bin nicht ganz sicher, wo ich das erste Mal darüber gelesen habe, aber ich habe es seitdem nicht mehr gesehen. YMMV.

11
Jonathon Watney

Eine andere schnelle Möglichkeit, einen benutzerdefinierten Wert für ServiceName und DisplayName anzugeben, ist die Verwendung von installutil Befehlszeilenparametern.

  1. Überschreiben Sie in Ihrer Klasse ProjectInstaller die virtuellen Methoden Install(IDictionary stateSaver) und Uninstall(IDictionary savedState)

    public override void Install(System.Collections.IDictionary stateSaver)
    {
        GetCustomServiceName();
        base.Install(stateSaver);
    }
    
    public override void Uninstall(System.Collections.IDictionary savedState)
    {
        GetCustomServiceName();
        base.Uninstall(savedState);
    }
    
    //Retrieve custom service name from installutil command line parameters
    private void GetCustomServiceName()
    {
        string customServiceName = Context.Parameters["servicename"];
        if (!string.IsNullOrEmpty(customServiceName))
        {
            serviceInstaller1.ServiceName = customServiceName;
            serviceInstaller1.DisplayName = customServiceName;
        }
    }
    
  2. Bauen Sie Ihr Projekt
  3. Installieren Sie den Dienst mit installutil und fügen Sie Ihren benutzerdefinierten Namen mit dem Parameter /servicename Hinzu:

    installutil.exe /servicename="CustomServiceName" "c:\pathToService\SrvcExecutable.exe"
    

Bitte beachten Sie, dass der Dienst mit den in ProjectInstaller properties/config angegebenen Werten für ServiceName und DisplayName installiert wird, wenn Sie in der Befehlszeile nicht /servicename Angeben

7
Andrea

Ich hatte nicht viel Glück mit den oben genannten Methoden, als ich unsere automatisierte Bereitstellungssoftware zum häufigen Installieren/Deinstallieren von Side-by-Side-Windows-Diensten verwendete. Schließlich kam ich zu folgendem Ergebnis, mit dem ich einen Parameter zur Angabe eines Suffix übergeben konnte auf den Dienstnamen in der Befehlszeile. Dies ermöglicht es dem Designer auch, ordnungsgemäß zu funktionieren, und kann bei Bedarf leicht angepasst werden, um den gesamten Namen zu überschreiben.

public partial class ProjectInstaller : System.Configuration.Install.Installer
{
  protected override void OnBeforeInstall(IDictionary savedState)
  {
    base.OnBeforeInstall(savedState);
    SetNames();
  }

  protected override void OnBeforeUninstall(IDictionary savedState)
  {
    base.OnBeforeUninstall(savedState);
    SetNames();
  }

  private void SetNames()
  {
    this.serviceInstaller1.DisplayName = AddSuffix(this.serviceInstaller1.DisplayName);
    this.serviceInstaller1.ServiceName = AddSuffix(this.serviceInstaller1.ServiceName);
  }

  private string AddSuffix(string originalName)
  {
    if (!String.IsNullOrWhiteSpace(this.Context.Parameters["ServiceSuffix"]))
      return originalName + " - " + this.Context.Parameters["ServiceSuffix"];
    else
      return originalName;
  }
}

In diesem Sinne kann ich Folgendes tun: Wenn ich den Dienst "Awesome Service" genannt habe, kann ich eine UAT-Version des Dienstes wie folgt installieren:

InstallUtil.exe /ServiceSuffix="UAT" MyService.exe

Dadurch wird der Dienst mit dem Namen "Awesome Service - UAT" erstellt. Wir haben dies verwendet, um DEVINT-, TESTING- und ACCEPTANCE-Versionen desselben Dienstes gleichzeitig auf einem einzelnen Computer auszuführen. Jede Version hat einen eigenen Satz von Dateien/Konfigurationen. Ich habe nicht versucht, mehrere Dienste zu installieren, die auf denselben Satz von Dateien verweisen.

HINWEIS: Sie müssen das gleiche /ServiceSuffix, um den Dienst zu deinstallieren. Führen Sie zum Deinstallieren die folgenden Schritte aus:

InstallUtil.exe /u /ServiceSuffix="UAT" MyService.exe

5
tristankoffee

Um die perfekte Antwort von @ chris.house.00 this zu verbessern, können Sie folgende Funktion in Betracht ziehen, um sie aus Ihren App-Einstellungen zu lesen:

 public void GetServiceAndDisplayName(out string serviceNameVar, out string displayNameVar)
        {
            string configurationFilePath = Path.ChangeExtension(Assembly.GetExecutingAssembly().Location, "exe.config");
            XmlDocument doc = new XmlDocument();
            doc.Load(configurationFilePath);

            XmlNode serviceName = doc.SelectSingleNode("//appSettings//add[@key='ServiceName']");
            XmlNode displayName = doc.SelectSingleNode("//appSettings//add[@key='DisplayName']");


            if (serviceName != null && (serviceName.Attributes != null && (serviceName.Attributes["value"] != null)))
            {
                serviceNameVar = serviceName.Attributes["value"].Value;
            }
            else
            {
                serviceNameVar = "Custom.Service.Name";
            }

            if (displayName != null && (displayName.Attributes != null && (displayName.Attributes["value"] != null)))
            {
                displayNameVar = displayName.Attributes["value"].Value;
            }
            else
            {
                displayNameVar = "Custom.Service.DisplayName";
            }
        }
4
Teoman shipahi

Ich habe dafür gesorgt, dass der Dienstname und der Anzeigename in einer app.config für meinen Dienst gespeichert werden. Dann lade ich in meiner Installer-Klasse die app.config als XmlDocument und verwende xpath, um die Werte auszugeben und auf ServiceInstaller.ServiceName und ServiceInstaller.DisplayName anzuwenden, bevor ich InitializeComponent () aufrufe. Dies setzt voraus, dass Sie diese Eigenschaften noch nicht in InitializeComponent () festgelegt haben. In diesem Fall werden die Einstellungen aus Ihrer Konfigurationsdatei ignoriert. Der folgende Code wird von meinem Installer-Klassenkonstruktor vor InitializeComponent () aufgerufen:

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

Ich glaube nicht, dass das Lesen der Konfigurationsdatei direkt aus ConfigurationManager.AppSettings oder etwas Ähnlichem funktioniert, wenn das Installationsprogramm ausgeführt wird. Es wird im Kontext von InstallUtil.exe ausgeführt, nicht im Kontext der .exe Ihres Dienstes. Möglicherweise können Sie mit ConfigurationManager.OpenExeConfiguration etwas tun. In meinem Fall hat dies jedoch nicht funktioniert, da ich versucht habe, einen benutzerdefinierten Konfigurationsabschnitt aufzurufen, der nicht geladen wurde.

4
chris.house.00

Ich hatte eine ähnliche Situation, in der ich einen vorherigen Dienst und einen aktualisierten Dienst benötigt habe, die nebeneinander auf demselben Server ausgeführt werden. (Es war mehr als nur eine Datenbankänderung, es waren auch Codeänderungen). Ich konnte also nicht einfach dieselbe .exe zweimal ausführen. Ich brauchte eine neue .exe-Datei, die mit neuen DLLs kompiliert wurde, aber aus demselben Projekt stammt. Das Ändern des Dienstnamens und des Anzeigenamens des Dienstes hat bei mir nicht funktioniert. Ich habe dennoch den Fehler "Dienst existiert bereits" erhalten, der meiner Ansicht nach darauf zurückzuführen ist, dass ich ein Bereitstellungsprojekt verwende. Was letztendlich für mich funktioniert hat, ist, dass es in den Eigenschaften meines Bereitstellungsprojekts eine Eigenschaft namens "ProductCode" gibt, bei der es sich um eine Guid handelt.

enter image description here

Erstellen Sie anschließend das Setup-Projekt neu, um eine neue EXE- oder MSI-Datei zu erstellen, die erfolgreich installiert wurde.

2
cmartin

Der einfachste Ansatz besteht darin, den Dienstnamen auf dem DLL-Namen zu basieren:

string sAssPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
string sAssName = System.IO.Path.GetFileNameWithoutExtension(sAssPath);
if ((this.ServiceInstaller1.ServiceName != sAssName)) {
    this.ServiceInstaller1.ServiceName = sAssName;
    this.ServiceInstaller1.DisplayName = sAssName;
}
1
Igor Krupitsky