remarks - generate documentation c#
¿Cuál es la forma correcta de que falle un servicio de Windows? (5)
Descubrí que Environment.Exit (1) funciona bien para mí. Generalmente lo ubico en un método que detecta excepciones no controladas y registra el problema antes de que lo detenga. Destruye completamente el servicio, pero el SCM también sabe que está apagado. Puede configurar el SCM para reiniciar su servicio automáticamente cuando se apaga x cantidad de veces. Encuentro que esto es mucho más útil que escribir tu propio código de reinicio / apagado.
Heredé un servicio de Windows escrito en C #. En raras condiciones, falla mal. Sin embargo, no está del todo claro cómo fracasar bien. Ross Bennett declara el problema elegantemente en bytes.com . Por simplicidad, lo citaré aquí.
Ahoy, amigos!
He estado buscando todo esto, pero parece que no puedo sacudir ninguna documentación de MSDN o de Google. Revisé cada artículo de .NET sobre el desarrollo de servicios de Windows en el MSDN que he localizado.
Estoy desarrollando una aplicación de servicio de Windows. Este servicio lee sus datos de configuración del registro del sistema (HKLM) donde fue depositado por otra aplicación "gerente". No hay problemas allí.
El servicio usa un hilo de trabajo para hacer su trabajo. El hilo se crea en OnStart () y se señala / une / elimina en OnStop (). Nuevamente, no hay problemas.
Todo funciona maravillosamente cuando:
- El administrador del sistema ha configurado todo correctamente, y
- los recursos de la red extranjera son accesibles.
Pero, por supuesto, nosotros, como desarrolladores, simplemente no podemos confiar en:
- El administrador del sistema ha configurado todo correctamente, o
- los recursos de la red extranjera son accesibles.
Realmente, lo que necesitamos es que la aplicación de servicio tenga alguna manera de morir por sí misma. Si un recurso de red falla, necesitamos que el servicio se detenga. Pero más al grano, necesitamos que el SCM sepa que se ha detenido por sí mismo. SCM necesita saber que el servicio ha "fallado" ... y alguien no lo ha cerrado.
Llamar a "devolver" o lanzar una excepción en el método "OnStart ()" ni siquiera es útil para los servicios que aún están en el proceso de inicio. El SCM continúa alegremente y el proceso sigue ejecutándose en el Administrador de tareas, aunque es no hacer nada en realidad ya que el hilo de trabajo nunca se creó y se inició.
El uso de una instancia de ServiceController tampoco lo hace. Eso le parece al SCM como un apagado normal, no una falla del servicio. Entonces ninguna de las acciones de recuperación o reinicios ocurren. (Además, hay documentación de MSDNful que advierte sobre los peligros de un descendiente de ServiceBase que usa un ServiceController para hacer que las cosas sucedan).
He leído artículos en los que la gente se metía con llamadas Invocadoras al código nativo solo para configurar el indicador de estado "Detenido" en el SCM. Pero eso no cierra el proceso en el que se ejecuta el servicio.
Realmente me gustaría saber la forma prevista de:
- Cerrar un servicio desde dentro del servicio, donde
- El SCM es notificado apropiadamente de que el servicio ha "detenido", y
- El proceso desaparece del Administrador de tareas.
Las soluciones que implican ServiceControllers no parecen ser apropiadas, aunque solo sea porque 2 no está satisfecho. (Que la documentación del Marco específicamente contraindica hacer eso tiene un gran peso, por cierto).
Agradecería cualquier recomendación, consejos para la documentación o incluso una conjetura bien razonada. :-) ¡Oh! Y estoy perfectamente feliz de entretener que me he perdido el punto.
Muy cordialmente
Ross Bennett
Después de algunas pruebas, encontré los siguientes trabajos en los casos en los que llamar a Stop podría causar otros problemas:
ExitCode = 1;
Environment.Exit(1);
Llamar a Environment.Exit no hace que el SCM maneje los fallos, pero primero establece el ServiceBase ExitCode.
La mejor práctica en el código nativo es llamar a SetServiceStatus con un código de salida distinto de cero para indicar 1) se detuvo y 2) algo salió mal.
En el código administrado, puede obtener el mismo efecto obteniendo el identificador SCM a través de la Propiedad ServiceBase.ServiceHandle y P / Invocación de la API Win32.
No veo por qué el SCM trataría esto de forma diferente a la configuración de la propiedad ServiceBase.ExitCode
distinta de cero y luego llamando a ServiceBase.Stop
, en realidad. P / Invoke es un poco más directo, tal vez, si el servicio está en modo de pánico.
No sé si hay un equivalente (no P / invocación) para esto, pero la forma de WinAPI parece ser llamar a SetServiceStatus
con un valor de SERVICE_STOPPED
y luego esperar a que SCM lo cierre. Como efecto secundario positivo, registra la falla de su servicio en el registro de eventos.
Estas son algunas citas de la parte relevante de la documentación :
Si un servicio llama a SetServiceStatus con el miembro dwCurrentState establecido en SERVICE_STOPPED y el miembro dwWin32ExitCode establecido en un valor distinto de cero, la siguiente entrada se escribe en el registro de eventos del sistema:
[...] <ServiceName> terminado con el siguiente error: <ExitCode> [...]
Las siguientes son las mejores prácticas al llamar a esta función:
[...]
- Si el estado es SERVICE_STOPPED, realice toda la limpieza necesaria y llame a SetServiceStatus solo una vez. Esta función realiza una llamada LRPC al SCM. La primera llamada a la función en el estado SERVICE_STOPPED cierra el identificador de contexto RPC y cualquier llamada subsiguiente puede hacer que el proceso se bloquee.
- No intente realizar ningún trabajo adicional después de llamar a SetServiceStatus con SERVICE_STOPPED, porque el proceso de servicio se puede finalizar en cualquier momento.
PD: En mi opinión, si los recursos de la red no están disponibles, el servicio no debería detenerse, sino continuar ejecutándose, a la espera de que los recursos estén disponibles. Pueden ocurrir interrupciones temporarias de la red, y no deberían requerir intervención manual del administrador del sistema una vez que la red se haya realizado nuevamente.
Puede obtener el ExitCode correcto como se describe here . Entonces, el administrador de servicios de Windows dará el texto de error correcto.
Mi OnStart en ServiceBase se ve así:
protected override void OnStart(string[] args)
{
try
{
DoStart();
}
catch (Exception exp)
{
Win32Exception w32ex = exp as Win32Exception;
if (w32ex == null)
{
w32ex = exp.InnerException as Win32Exception;
}
if (w32ex != null)
{
ExitCode = w32ex.ErrorCode;
}
Stop();
}
}