Instalación de varias instancias del mismo servicio de Windows en un servidor


Así que hemos producido un servicio de Windows para alimentar datos a nuestra aplicación cliente y todo va muy bien. El cliente ha creado una divertida solicitud de configuración que requiere dos instancias de este servicio ejecutándose en el mismo servidor y configuradas para apuntar a bases de datos separadas.

Hasta ahora no he podido conseguir que esto suceda y esperaba que mis compañeros miembros de stackoverflow pudieran dar algunas pistas sobre el por qué.

Configuración actual:

He configurado el proyecto que contiene el servicio de Windows, lo llamaremos AppService a partir de ahora, y el ProjectInstaller.archivo cs que maneja pasos de instalación personalizados para establecer el nombre del servicio en función de una clave en la aplicación.config así:

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

En este caso Util es solo una clase estática que carga el nombre del servicio desde el archivo de configuración.

De aquí en adelante he intentado dos maneras diferentes de obtener ambos servicios instalados y ambos han fallado de una manera idéntica.

La primera manera fue simplemente instale la primera copia del servicio, copie el directorio instalado y cambie su nombre, y luego ejecute el siguiente comando después de modificar la configuración de la aplicación para cambiar el nombre del servicio deseado:

InstallUtil.exe /i AppService.exe

Cuando eso no funcionó, intenté crear un segundo proyecto de instalador, edité el archivo de configuración y construí el segundo instalador. Cuando ejecuté el instalador funcionó bien, pero el servicio no apareció en los servicios.msc así que ejecuté el comando anterior contra el segundo código instalado basar.

Ambas veces recibí la siguiente salida de InstallUtil (solo partes relevantes):

Ejecutando una instalación procesada.

Comenzando la fase de instalación de la instalación.

Instalación del servicio App Service Two... Service App Service Two se ha instalado correctamente. Creación de EventLog source App Service Two en la aplicación log...

Se produjo una excepción durante la fase de instalación. Sistema.NullReferenceException: Objeto referencia no establecida en una instancia de un objeto.

La fase de reversión de la instalación está comenzando.

Restaurar el registro de eventos al estado anterior para el Servicio de aplicación de origen Dos. El Servicio Dos de la aplicación de servicio se está eliminando del sistema... Service App Service Two se eliminó con éxito del sistema.

La fase de reversión se completó con éxito.

La instalación procesada se ha completado. La instalación falló, y la reversión ha sido realizar.

Lo siento por el post largo, quería asegurarse de que hay suficiente información relevante. La pieza que hasta ahora me tiene perpleja es que afirma que la instalación del servicio se completa con éxito y solo después de que vaya a crear la fuente EventLog que la excepción NullReferenceException parece ser lanzada. Así que si alguien sabe lo que estoy haciendo mal o tiene un mejor enfoque sería muy apreciado.

Author: Switters, 2009-08-14

10 answers

¿Ha probado el sc / service controller util? Tipo

sc create

En una línea de comandos, y le dará la entrada de ayuda. Creo que he hecho esto en el pasado para Subversion y usado este artículo como referencia:

Http://svn.apache.org/repos/asf/subversion/trunk/notes/windows-service.txt

 80
Author: jamesaharvey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-27 10:19:00

Puede ejecutar varias versiones del mismo servicio haciendo lo siguiente:

1) Copie el ejecutable del Servicio y config a su propia carpeta.

2) Copiar instalación.Exe a la carpeta ejecutable del servicio (de la carpeta. net framework)

3) Cree un archivo de configuración llamado Install.exe.configuración en la carpeta ejecutable del servicio con los siguientes contenidos (nombres de servicio únicos):

<?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) Cree un archivo por lotes para instalar el servicio con lo siguiente contenido:

REM Install
InstallUtil.exe YourService.exe
pause

5) Mientras está allí, cree un archivo por lotes de desinstalación

REM Uninstall
InstallUtil.exe -u YourService.exe
pause

EDITAR:

Tenga en cuenta que si me perdí algo, aquí está la clase ServiceInstaller (ajuste según sea necesario):

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
Author: Mark Redman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-15 07:56:31
  sc create [servicename] binpath= [path to your exe]

Esta solución funcionó para mí.

 16
Author: Rajesh Kumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-14 07:40:55

Vieja pregunta, lo sé, pero he tenido suerte usando la opción /servicename en InstallUtil.exe. Sin embargo, no lo veo en la ayuda incorporada.

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

No estoy del todo seguro de dónde leí por primera vez sobre esto, pero no lo he visto desde entonces. YMMV.

 11
Author: Jonathon Watney,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-22 20:25:40

No tuve mucha suerte con los métodos anteriores al usar nuestro software de implementación automatizada para instalar/desinstalar con frecuencia servicios de Windows lado a lado, pero finalmente se me ocurrió lo siguiente que me permite pasar un parámetro para especificar un sufijo al nombre del servicio en la línea de comandos. También permite que el diseñador funcione correctamente y podría adaptarse fácilmente para anular el nombre completo si es necesario.

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

Con esto en mente, puedo hacer lo siguiente: Si he llamado el servicio "Awesome Service", entonces puedo instalar una versión UAT del servicio de la siguiente manera:

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

Esto creará el servicio con el nombre "Awesome Service - UAT". Hemos utilizado esto para ejecutar DEVINT, PRUEBAS y versiones de ACEPTACIÓN del mismo servicio que se ejecutan lado a lado en una sola máquina. Cada versión tiene su propio conjunto de archivos / configuraciones - no he intentado esto para instalar varios servicios apuntando al mismo conjunto de archivos.

NOTA: tienes que usar lo mismo /ServiceSuffix parámetro para desinstalar el servicio, por lo que ejecutaría lo siguiente para desinstalar:

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

 5
Author: tristankoffee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-19 17:57:09

Lo que he hecho para que esto funcione es almacenar el nombre del servicio y el nombre para mostrar en una aplicación.config para mi servicio. Luego, en mi clase de instalador, cargo la aplicación.config como un XmlDocument y use xpath para obtener los valores y aplicarlos a ServiceInstaller.ServiceName y ServiceInstaller.DisplayName, antes de llamar a InitializeComponent (). Esto asume que no estás configurando estas propiedades en InitializeComponent (), en cuyo caso, la configuración de tu archivo de configuración será ignorada. El el siguiente código es lo que estoy llamando desde mi constructor de clase installer, antes de 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;
          }
      }

No creo leer el archivo de configuración directamente desde ConfigurationManager.AppSettings o algo similar funcionará como cuando se ejecuta el instalador, se ejecuta en el contexto de InstallUtil.exe, no de tu servicio .exe. Es posible que pueda hacer algo con ConfigurationManager.OpenExeConfiguration, sin embargo, en mi caso, esto no funcionó ya que estaba tratando de llegar a una costumbre sección de configuración que no se cargó.

 4
Author: chris.house.00,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-24 16:42:30

Solo para mejorar la respuesta perfecta de @chris.casa.00 este , puede considerar la siguiente función para leer desde la configuración de su aplicación:

 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
Author: Teoman shipahi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:57

Otra forma rápida de especificar un valor personalizado para ServiceName y DisplayName es usar parámetros de línea de comandos installutil.

  1. En su clase ProjectInstaller invalida los métodos virtuales Install(IDictionary stateSaver) y 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. Construye tu proyecto
  3. Instale el servicio con installutil agregando su nombre personalizado usando el parámetro /servicename:

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

Tenga en cuenta que si no especifica /servicename en la línea de comandos, el servicio se instalará con ServiceName y Valores de DisplayName especificados en ProjectInstaller properties / config

 4
Author: Andrea,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-22 08:25:27

Tuve una situación similar, donde necesitaba tener un servicio anterior, y un servicio actualizado corriendo lado a lado en el mismo servidor. (Fue más que un simple cambio de base de datos, también fueron cambios de código). Así que no podía hacer lo mismo .exe dos veces. Necesitaba una nueva .exe que fue compilado con nuevas DLL pero del mismo proyecto. Simplemente cambiar el nombre del servicio y el nombre de visualización del servicio no funcionó para mí, todavía recibí el "error de servicio ya existía" que creo que es porque estoy usando un Proyecto de Implementación. Lo que finalmente funcionó para mí es que dentro de las Propiedades de mi Proyecto de implementación hay una propiedad llamada "ProductCode" que es un Guid.

introduzca la descripción de la imagen aquí

Después de eso, reconstruir el proyecto de instalación a un nuevo.exe or .msi instalado correctamente.

 2
Author: cmartin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-30 14:19:15

El enfoque más simple es que se basa el nombre del servicio en el nombre dll:

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
Author: Igor Krupitsky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-29 21:27:09