¿Cómo hacer que un servicio de Windows .NET se inicie inmediatamente después de la instalación?

88

Además del service.StartType = ServiceStartMode.Automatic, mi servicio no se inicia después de la instalación

Solución

Inserté este código en mi ProjectInstaller

protected override void OnAfterInstall(System.Collections.IDictionary savedState)
{
    base.OnAfterInstall(savedState);
    using (var serviceController = new ServiceController(this.serviceInstaller1.ServiceName, Environment.MachineName))
        serviceController.Start();
}

Gracias a ScottTx y Francis B.

Jader Dias
fuente
¿No se inicia inmediatamente después de la instalación o no se inicia cuando reinicia?
Chris Van Opstal

Respuestas:

21

Puede hacer todo esto desde el ejecutable de su servicio en respuesta a eventos disparados desde el proceso InstallUtil. Reemplace el evento OnAfterInstall para usar una clase ServiceController para iniciar el servicio.

http://msdn.microsoft.com/en-us/library/system.serviceprocess.serviceinstaller.aspx

ScottTx
fuente
3
Esta es una buena solución, pero aún requiere el uso de la utilidad InstallUtil. Si ya está entregando InstallUtil como parte de su instalación, esto tiene más sentido. Pero, si desea renunciar a empaquetar InstallUtil, use la solución de línea de comandos.
Matt Davis
181

He publicado un procedimiento paso a paso para crear un servicio de Windows en C # aquí . Parece que al menos ha llegado a este punto y ahora se pregunta cómo iniciar el servicio una vez que esté instalado. Establecer la propiedad StartType en Automático hará que el servicio se inicie automáticamente después de reiniciar su sistema, pero no iniciará automáticamente su servicio (como ha descubierto) después de la instalación.

No recuerdo dónde lo encontré originalmente (¿quizás Marc Gravell?), Pero encontré una solución en línea que le permite instalar e iniciar su servicio ejecutando su propio servicio. Aquí está el paso a paso:

  1. Estructura la Main()función de tu servicio así:

    static void Main(string[] args)
    {
        if (args.Length == 0) {
            // Run your service normally.
            ServiceBase[] ServicesToRun = new ServiceBase[] {new YourService()};
            ServiceBase.Run(ServicesToRun);
        } else if (args.Length == 1) {
            switch (args[0]) {
                case "-install":
                    InstallService();
                    StartService();
                    break;
                case "-uninstall":
                    StopService();
                    UninstallService();
                    break;
                default:
                    throw new NotImplementedException();
            }
        }
    }
    
  2. Aquí está el código de apoyo:

    using System.Collections;
    using System.Configuration.Install;
    using System.ServiceProcess;
    
    private static bool IsInstalled()
    {
        using (ServiceController controller = 
            new ServiceController("YourServiceName")) {
            try {
                ServiceControllerStatus status = controller.Status;
            } catch {
                return false;
            }
            return true;
        }
    }
    
    private static bool IsRunning()
    {
        using (ServiceController controller = 
            new ServiceController("YourServiceName")) {
            if (!IsInstalled()) return false;
            return (controller.Status == ServiceControllerStatus.Running);
        }
    }
    
    private static AssemblyInstaller GetInstaller()
    {
        AssemblyInstaller installer = new AssemblyInstaller(
            typeof(YourServiceType).Assembly, null);
        installer.UseNewContext = true;
        return installer;
    }
    
  3. Continuando con el código de apoyo ...

    private static void InstallService()
    {
        if (IsInstalled()) return;
    
        try {
            using (AssemblyInstaller installer = GetInstaller()) {
                IDictionary state = new Hashtable();
                try {
                    installer.Install(state);
                    installer.Commit(state);
                } catch {
                    try {
                        installer.Rollback(state);
                    } catch { }
                    throw;
                }
            }
        } catch {
            throw;
        }
    }
    
    private static void UninstallService()
    {
        if ( !IsInstalled() ) return;
        try {
            using ( AssemblyInstaller installer = GetInstaller() ) {
                IDictionary state = new Hashtable();
                try {
                    installer.Uninstall( state );
                } catch {
                    throw;
                }
            }
        } catch {
            throw;
        }
    }
    
    private static void StartService()
    {
        if ( !IsInstalled() ) return;
    
        using (ServiceController controller = 
            new ServiceController("YourServiceName")) {
            try {
                if ( controller.Status != ServiceControllerStatus.Running ) {
                    controller.Start();
                    controller.WaitForStatus( ServiceControllerStatus.Running, 
                        TimeSpan.FromSeconds( 10 ) );
                }
            } catch {
                throw;
            }
        }
    }
    
    private static void StopService()
    {
        if ( !IsInstalled() ) return;
        using ( ServiceController controller = 
            new ServiceController("YourServiceName")) {
            try {
                if ( controller.Status != ServiceControllerStatus.Stopped ) {
                    controller.Stop();
                    controller.WaitForStatus( ServiceControllerStatus.Stopped, 
                         TimeSpan.FromSeconds( 10 ) );
                }
            } catch {
                throw;
            }
        }
    }
    
  4. En este punto, después de instalar su servicio en la máquina de destino, simplemente ejecute su servicio desde la línea de comando (como cualquier aplicación común) con el -installargumento de la línea de comando para instalar e iniciar su servicio.

Creo que he cubierto todo, pero si encuentra que esto no funciona, hágamelo saber para que pueda actualizar la respuesta.

Matt Davis
fuente
12
Tenga en cuenta que esta solución no requiere el uso de InstallUtil.exe, por lo que no tiene que entregarlo como parte de su programa de instalación.
Matt Davis
3
¿Cuál es el punto con las cláusulas vacías "catch {throw;}"? Además, probablemente no sea una buena idea ocultar las fallas con "Rollback ()" ya que esa situación básicamente deja al sistema en un estado indefinido, supongo (intentaste instalar un servicio, fallaste en algún punto intermedio y no pudiste deshacerlo ). Debería al menos "mostrar" al usuario que hay algo sospechoso - ¿o la función Rollback () escribe algunos mensajes en la consola?
Christian.K
5
La reversión escribe datos en la consola. En cuanto a los bloques de captura vacíos, es una cuestión de depuración. Puedo poner un punto de interrupción en la declaración de lanzamiento para examinar cualquier excepción que pueda ocurrir.
Matt Davis
4
Recibo un error Error: no se pudo encontrar el tipo o el nombre del espacio de nombres 'YourServiceType' (¿le falta una directiva de uso o una referencia de ensamblaje?
Yogesh
5
YourServiceTypees el ProjectInstallerque agregó al servicio que contiene ServiceInstalleryServiceProcessInstaller
bansi
6

Estudio visual

Si está creando un proyecto de instalación con VS, puede crear una acción personalizada que llamó a un método .NET para iniciar el servicio. Pero, realmente no se recomienda utilizar acciones personalizadas administradas en un MSI. Vea esta página .

ServiceController controller  = new ServiceController();
controller.MachineName = "";//The machine where the service is installed;
controller.ServiceName = "";//The name of your service installed in Windows Services;
controller.Start();

InstallShield o Wise

Si está utilizando InstallShield o Wise, estas aplicaciones ofrecen la opción de iniciar el servicio. Por ejemplo, con Wise, debe agregar una acción de control de servicio. En esta acción, especifica si desea iniciar o detener el servicio.

Wix

Al usar Wix, debe agregar el siguiente código xml bajo el componente de su servicio. Para obtener más información al respecto, puede consultar esta página .

<ServiceInstall 
    Id="ServiceInstaller"  
    Type="ownProcess"  
    Vital="yes"  
    Name=""  
    DisplayName=""  
    Description=""  
    Start="auto"  
    Account="LocalSystem"   
    ErrorControl="ignore"   
    Interactive="no">  
        <ServiceDependency Id="????"/> ///Add any dependancy to your service  
</ServiceInstall>
Francis B.
fuente
5

Debe agregar una acción personalizada al final de la secuencia 'ExecuteImmediate' en el MSI, utilizando el nombre del componente del EXE o un lote (sc start) como fuente. No creo que esto se pueda hacer con Visual Studio, es posible que deba usar una herramienta de autoría MSI real para eso.

Otávio Décio
fuente
4

Para iniciarlo justo después de la instalación, genero un archivo por lotes con installutil seguido de sc start

No es ideal, pero funciona ...

Mate
fuente
4

Use la clase .NET ServiceController para iniciarlo, o emita el comando de línea de comandos para iniciarlo --- "net start servicename". De cualquier manera funciona.

ScottTx
fuente
4

Para agregar a la respuesta de ScottTx, aquí está el código real para iniciar el servicio si lo está haciendo a la manera de Microsoft (es decir, usando un proyecto de instalación, etc.)

(disculpe el código de VB.net, pero esto es con lo que estoy atrapado)

Private Sub ServiceInstaller1_AfterInstall(ByVal sender As System.Object, ByVal e As System.Configuration.Install.InstallEventArgs) Handles ServiceInstaller1.AfterInstall
    Dim sc As New ServiceController()
    sc.ServiceName = ServiceInstaller1.ServiceName

    If sc.Status = ServiceControllerStatus.Stopped Then
        Try
            ' Start the service, and wait until its status is "Running".
            sc.Start()
            sc.WaitForStatus(ServiceControllerStatus.Running)

            ' TODO: log status of service here: sc.Status
        Catch ex As Exception
            ' TODO: log an error here: "Could not start service: ex.Message"
            Throw
        End Try
    End If
End Sub

Para crear el controlador de eventos anterior, vaya al diseñador de ProjectInstaller donde están los 2 controles. Haga clic en el control ServiceInstaller1. Vaya a la ventana de propiedades debajo de eventos y allí encontrará el evento AfterInstall.

Nota: No coloque el código anterior en el evento AfterInstall para ServiceProcessInstaller1. No funcionará, viniendo de la experiencia. :)

goku_da_master
fuente
¡El código de VB.net no está mal! Para aquellos de nosotros que trabajamos en varios idiomas, ¡es bueno no tener que convertir el código de C!
Steve Reed Sr
Gracias, esto me ayudó a descubrir cómo iniciar automáticamente un servicio.
Charles Owen
0

La solución más sencilla se encuentra aquí install-windows-service-without-installutil-exe por @ Hoàng Long

@echo OFF
echo Stopping old service version...
net stop "[YOUR SERVICE NAME]"
echo Uninstalling old service version...
sc delete "[YOUR SERVICE NAME]"

echo Installing service...
rem DO NOT remove the space after "binpath="!
sc create "[YOUR SERVICE NAME]" binpath= "[PATH_TO_YOUR_SERVICE_EXE]" start= auto
echo Starting server complete
pause
Robert Green MBA
fuente