¿Cómo puede un servicio de Windows reiniciarse mediante programación?

Respuestas:

187

Configure el servicio para que se reinicie después de un error (haga doble clic en el servicio en el panel de control y eche un vistazo a esas pestañas; olvido el nombre). Luego, cada vez que desee que se reinicie el servicio, simplemente llame Environment.Exit(1)(o cualquier devolución que no sea cero) y el sistema operativo lo reiniciará por usted.

TheSoftwareJedi
fuente
77
Para su información, la ubicación está en el panel de servicios, haga clic con el botón derecho en el servicio en cuestión y seleccione propiedades, luego elija la pestaña de recuperación.
James Michael Hare
20
Veo cómo esto logra el comportamiento deseado, pero un código de retorno de 1 está destinado a decirle al sistema que hubo un error. ¿No es una mala práctica si de hecho no hubo ningún error y solo desea reiniciar su servicio?
mgttlinger
44
El instalador del servicio puede hacer esto programáticamente en el evento posterior a la instalación, sin necesidad de hacer clic ... ¿Qué sucede si su servicio está en un servidor remoto y necesita instalarlo varias veces al día, por ejemplo, durante las pruebas?
Dean Kuga
55
@mgttlinger: Creo que sí, es una mala práctica cuando su servicio es saludable, por lo que nunca será necesario reiniciarlo. Pero algunas arquitecturas de servicios están fuera de nuestro alcance y si necesitan reiniciarse es un síntoma de que no está bien, por lo que no hay problema para cerrar y liberar algunos recursos mínimos (si es posible) y 'cortar los pies', ya que dejar el El servicio que se ejecuta incorrectamente puede ser peor (inútil).
Luciano
55
@JohnLeidegren si desea un apagado adecuado, puede configurar Service.ExitCode = 1y luego ejecutar Service.Stop()junto con la casilla de verificación 'Habilitar acciones para paradas con errores' en la pantalla de configuración de recuperación del servicio. Hacerlo de esta manera permite un apagado adecuado y aún así activa el reinicio.
Chris Rice
22
Dim proc As New Process()
Dim psi As New ProcessStartInfo()

psi.CreateNoWindow = True
psi.FileName = "cmd.exe"
psi.Arguments = "/C net stop YOURSERVICENAMEHERE && net start YOURSERVICENAMEHERE"
psi.LoadUserProfile = False
psi.UseShellExecute = False
psi.WindowStyle = ProcessWindowStyle.Hidden
proc.StartInfo = psi
proc.Start()
Khalid Rahaman
fuente
3
Recuerde agregar "" cerca de su nombre de servicio si contiene espacios.
apc
44
Esto es solo para servicios que se ejecutan con una cuenta privilegiada, lo cual es una mala idea de todos modos.
Dmitry Gusarov
17

No puede estar seguro de que la cuenta de usuario con la que se ejecuta su servicio tenga incluso permisos para detener y reiniciar el servicio.

Greg Hewgill
fuente
Aunque hice +1 cuando enfrenté el mismo problema, al mirar sc.exe resulta que utiliza SERVICE_QUERY_CONFIG como dwDesiredAccess cuando llama a OpenSCManager () y luego llama a OpenService () con SERVICE_START | SERVICE_STOP para abrir un servicio en particular y funciona bien (no hay problema con el acceso). No estoy seguro de cómo se puede hacer esto en .NET.
n0p
La mayoría de los servicios de Windows se ejecutan como sistema, por lo que no debería ser un problema.
Cambie el
@Switch hay un gran círculo de servicios que se ejecutan en SERVICIO DE RED, que por defecto ni siquiera tiene derechos para abrir su propio ejecutable.
AgentFire
@AgentFire, correcto, pero el valor predeterminado es Sistema local, no SERVICIO DE RED. El sistema local tiene inherentemente el nivel más alto de privilegios para el sistema operativo, superando los asignados a los miembros del grupo de administradores locales.
Cambie el
@Switch pero usar los privilegios más disponibles está violando el Principio PoL .
AgentFire
12

Puede crear un proceso que sea un símbolo del sistema de DOS que se reinicie:

 Process process = new Process();
 process.StartInfo.FileName = "cmd";
 process.StartInfo.Arguments = "/c net stop \"servicename\" & net start \"servicename\"";
 process.Start();
VladL
fuente
Esto, sin hacer nada más, heredará el contexto de seguridad del hilo de llamada ... y siempre que sea un contexto que tenga suficiente potencia para reiniciar, eres bueno.
Clay
12
const string strCmdText = "/C net stop \"SERVICENAME\"&net start \"SERVICENAME\"";
Process.Start("CMD.exe", strCmdText);

donde SERVICENAMEestá el nombre de su servicio (las comillas dobles incluidas para dar cuenta de los espacios en el nombre del servicio, de lo contrario, se pueden omitir).

Limpio, no es necesaria la configuración de reinicio automático.

Filip
fuente
9
aprendió algo nuevo sobre & vs &&: [command1] & [command2] siempre ejecutará ambos comandos secuencialmente, mientras que [command1] && [command2] ejecuta command2 solo si command1 se ejecuta con éxito ( autoitscript.com/forum/topic/… )
Jeffrey Caballero
5

Dependerá de por qué quiere que se reinicie.

Si solo está buscando una manera de que el servicio se limpie periódicamente, entonces podría tener un temporizador ejecutándose en el servicio que periódicamente provoca una rutina de purga.

Si está buscando una forma de reiniciar en caso de falla, el host del servicio en sí mismo puede proporcionar esa capacidad cuando se configura.

Entonces, ¿por qué necesita reiniciar el servidor? ¿Qué estás intentando lograr?

Brody
fuente
1
Esto no funcionaría si le preocupa el montón de objetos grandes y la fragmentación de la memoria. Supuestamente, los procesos .NET 4 / 4.5 y 64 bits han ayudado mucho con esto.
David Kassa
3

No creo que pueda hacerlo en un servicio autónomo (cuando llama a Reiniciar, detendrá el servicio, lo que interrumpirá el comando Reiniciar, y nunca volverá a comenzar). Si puede agregar un segundo .exe (una aplicación de consola que usa la clase ServiceManager), puede iniciar el .exe independiente y hacer que reinicie el servicio y luego salga.

Pensándolo bien, probablemente podría hacer que el servicio registre una Tarea Programada (usando el comando 'at' de la línea de comandos, por ejemplo) para iniciar el servicio y luego hacer que se detenga; eso probablemente funcionaría.

tecnófilo
fuente
3

El problema con el desplazamiento hacia un archivo por lotes o EXE es que un servicio puede tener o no los permisos necesarios para ejecutar la aplicación externa.

La forma más limpia de hacer esto que he encontrado es utilizar el método OnStop (), que es el punto de entrada para el Administrador de control de servicios. Luego, se ejecutará todo el código de limpieza y no tendrá ningún socket colgado u otros procesos, suponiendo que su código de detención esté haciendo su trabajo.

Para hacer esto, debe establecer un indicador antes de finalizar que le indique al método OnStop que salga con un código de error; entonces el SCM sabe que el servicio debe reiniciarse. Sin este indicador, no podrá detener el servicio manualmente desde SCM. Esto también supone que ha configurado el servicio para reiniciar en caso de error.

Aquí está mi código de parada:

...

bool ABORT;

protected override void OnStop()
{
    Logger.log("Stopping service");
    WorkThreadRun = false;
    WorkThread.Join();
    Logger.stop();
    // if there was a problem, set an exit error code
    // so the service manager will restart this
    if(ABORT)Environment.Exit(1);
}

Si el servicio se encuentra con un problema y necesita reiniciarse, inicio un subproceso que detiene el servicio desde el SCM. Esto permite que el servicio se limpie después de sí mismo:

...

if(NeedToRestart)
{
    ABORT = true;
    new Thread(RestartThread).Start();
}

void RestartThread()
{
    ServiceController sc = new ServiceController(ServiceName);
    try
    {
        sc.Stop();
    }
    catch (Exception) { }
}
usuario3235770
fuente
2

Usaría el Programador de Windows para programar un reinicio de su servicio. El problema es que no puede reiniciarse, pero puede detenerse. (Esencialmente has cortado la rama en la que estás sentado ... si entiendes mi analogía) Necesitas un proceso separado para hacerlo por ti. El Programador de Windows es apropiado. Programe una tarea única para reiniciar su servicio (incluso desde el propio servicio) para ejecutarlo de inmediato.

De lo contrario, tendrá que crear un proceso de "pastoreo" que lo haga por usted.

dviljoen
fuente
2

La primera respuesta a la pregunta es la solución más simple: "Environment.Exit (1)" Estoy usando esto en Windows Server 2008 R2 y funciona perfectamente. El servicio se detiene solo, el O / S espera 1 minuto y luego lo reinicia.

tangentMan
fuente
El tiempo de espera depende del tiempo que lo configure para esperar. Sin embargo, el valor predeterminado es 1 minuto, pero si alguien no quiere que su respuesta dé la impresión incorrecta.
Espen
1

No creo que pueda. Cuando un servicio se "detiene", se descarga totalmente.

Bueno, bien, siempre hay una forma, supongo. Por ejemplo, podría crear un proceso separado para detener el servicio, luego reiniciarlo y luego salir.

TED
fuente
0

El mejor enfoque puede ser utilizar el Servicio NT como envoltorio para su aplicación. Cuando se inicia el Servicio NT, su aplicación puede iniciarse en un modo "inactivo" esperando que se inicie el comando (o configurarse para iniciarse automáticamente).

Piense en un automóvil, cuando se enciende comienza en un estado inactivo, esperando que su comando avance o retroceda. Esto también permite otros beneficios, como una mejor administración remota, ya que puede elegir cómo exponer su aplicación.

plamb
fuente
0

Simplemente pasando: y pensé que agregaría información adicional ...

También puede lanzar una excepción, esto cerrará automáticamente el servicio de Windows, y las opciones de reinicio automático simplemente se activan. El único problema con esto es que si tiene un entorno de desarrollo en su PC, entonces el JIT intenta entrar en acción, y recibirá un mensaje que dice depurar S / N. diga no y luego se cerrará y luego se reiniciará correctamente. (en una PC sin JIT, todo funciona). La razón por la que estoy trolleando, es que este JIT es nuevo en Win 7 (solía funcionar bien con XP, etc.) y estoy tratando de encontrar una forma de deshabilitar el JIT ... Puedo probar el método Environment.Exit mencionado aquí, vea cómo Eso también funciona.

Kristian: Bristol, Reino Unido

Kristian
fuente
3
Todas estas soluciones de excepción de lanzamiento, salida (1) evitan la limpieza adecuada de una aplicación. Salir sin gracia es una solución pobre
devshorts del
0

Cree un archivo restart.bat como este

@echo on
set once="C:\Program Files\MyService\once.bat"
set taskname=Restart_MyService
set service=MyService
echo rem %time% >%once%
echo net stop %service% >>%once%
echo net start %service% >>%once%
echo del %once% >>%once%

schtasks /create /ru "System" /tn %taskname% /tr '%once%' /sc onstart /F /V1 /Z
schtasks /run /tn %taskname%

Luego elimine la tarea% taskname% cuando comience su% service%

Erik Martino
fuente
0

Cree un dominio de aplicación separado para alojar el código de la aplicación. Cuando se requiere reiniciar, podríamos descargar y volver a cargar el dominio de aplicación en lugar del proceso (servicio de Windows). Así es como funciona el grupo de aplicaciones IIS, no ejecutan la aplicación asp.net directamente, usan appdmain por separado.

CreativeManix
fuente
-2

La forma más fácil es tener un archivo por lotes con:

parada neta inicio neto

y agregue el archivo al planificador con el intervalo de tiempo deseado


fuente
No funciona porque el lote se detiene entre ambos comandos, porque es un proceso secundario del servicio en sí.
Orabîg
-3
private static void  RestartService(string serviceName)
    {
        using (var controller = new ServiceController(serviceName))
        {
            controller.Stop();
            int counter = 0;
            while (controller.Status != ServiceControllerStatus.Stopped)
            {
                Thread.Sleep(100);
                controller.Refresh();
                counter++;
                if (counter > 1000)
                {
                    throw new System.TimeoutException(string.Format("Could not stop service: {0}", Constants.Series6Service.WindowsServiceName));
                }
            }

            controller.Start();
        }
    }
Lee Smith
fuente