45 votes

Quelle est la bonne façon pour un service Windows d’échouer?

J'ai hérité d'un service Windows écrit en C#. Dans de rares cas, il va mal. Cependant, il n'est pas clair du tout comment l'échec de bien. Ross Bennett états le problème élégamment à bytes.com. Par souci de simplicité, je vais juste citer ici.

Ohé, Les Gens!

J'ai cherché partout, pour cela, mais je n'arrive pas à les secouer un la documentation de la MSDN ou de Google. J'ai passé en revue tous les .NET article sur le développement de Services de Windows dans la MSDN j'ai trouve.

Je suis le développement d'un Service Windows application. Ce service de son lit les données de configuration du système registre (HKLM) où il a été déposé par un autre "gestionnaire" de l'application. Pas de problèmes là-bas.

Le service utilise un thread de travail à faire ses travaux. Le thread est créé dans le OnStart() et a/joint/cédées dans le OnStop(). Encore une fois, pas de problèmes.

Tout fonctionne à merveille lorsque:

  1. L'administrateur système a tout mettre en place correctement, et
  2. l'étranger les ressources du réseau sont tous accessibles.

Mais bien sûr, nous, en tant que développeurs tout simplement ne pouvez pas compter sur:

  1. L'administrateur système ayant tout mis en place correctement, ou
  2. le réseau à l'étranger des ressources accessibles.

Vraiment, ce que nous avons besoin est pour la application de service à certains de mourir sur son propre. Si un réseau des ressources tombe en panne, nous avons besoin de l' arrêt du service. Mais plus de la point, nous avons besoin de la SCM à savoir qu'il a arrêté sur son propre accord. SCM besoins à savoir que le service a "a échoué"...et n'a pas simplement été fermé par quelqu'un.

L'appel de "retour" ou de jeter un exception dans le "OnStart()" méthode n'est même pas utile pour les services encore dans le processus de démarrage.. Le SCM va gaiement sur et le processus continue cours d'exécution dans le Gestionnaire des Tâches--si ce n'est pas en train de faire quoi que ce soit depuis le thread de travail n'a jamais été créé et a commencé.

À l'aide d'un ServiceController exemple ne pas le faire, soit. Qui semble le SCM comme un arrêt normal, et non pas une défaillance de service. Aucun les mesures de rétablissement ou de redémarrage de se produire. (Aussi, il est MSDNful documentation avertissement sur les dangers d'un ServiceBase descendant à l'aide d'un ServiceController pour rendre les choses arriver avec lui-même.)

J'ai lu des articles où les gens ont été bricoler avec PInvoking appels à le code natif pour le "Arrêté" de l'indicateur d'état dans le SCM. Mais cela ne veut pas arrêter le processus de la le service est en cours d'exécution à l'intérieur.

J'aimerais vraiment connaître la Destinée De:

  1. La fermeture d'un service à partir de l'intérieur du service, là où
  2. La SCM est appropriatedly averti que le service est "Stoppée", et
  3. Le processus disparaît à partir du Gestionnaire des Tâches.

Les Solutions impliquant ServiceControllers ne semble pas être le cas, si seulement parce que 2 n'est pas satisfait. (Que l' Cadre de la documentation spécifiquement contraindicates faire qui porte un beaucoup de poids, d'ailleurs.)

Je te remercie de toutes les recommandations, des pointeurs vers des documents ou même raisonnée des conjectures. :-) Oh! Et Je suis parfaitement heureux daccueillir que J'ai manqué le point.

Très cordialement,

Ross Bennett

38voto

Steve Townsend Points 36948

Les meilleures pratiques dans le code natif est d'appeler SetServiceStatus avec un code de sortie non nulle pour indiquer 1), il est arrêté et 2) quelque chose s'est mal passé.

Dans le code managé, vous pourriez obtenir le même effet par l'obtention de la SCM traiter dans le cadre de la ServiceBase.ServiceHandle de la Propriété et de P/Invoke-ing de l'API Win32.

Je ne vois pas pourquoi la SCM traitera différemment de réglage de l' ServiceBase.ExitCode de la propriété de non-zéro, puis en appelant ServiceBase.Stop, en fait. P/Invoke est un peu plus direct, peut-être, si le service est en mode panique.

6voto

Ryan Bennett Points 2602

J'ai trouvé que Environment.Exit (1) fonctionne bien pour moi. Je le place généralement dans une méthode qui intercepte les exceptions non gérées et enregistre le problème avant de l'arrêter. Il détruit complètement le service, mais le SCM sait également qu'il est à l'arrêt. Vous pouvez configurer le SCM pour redémarrer automatiquement votre service lorsqu'il descend x fois. Je trouve que c'est beaucoup plus utile que d'écrire votre propre code de redémarrage / arrêt.

3voto

Heinzi Points 66519

Je ne sais pas si il y a une (non-P/Invoke) équivalent à cela, mais la WinAPI voie semble être d'appeler SetServiceStatus avec une valeur de SERVICE_STOPPED , puis attendre que les SCM pour vous arrêtez vers le bas. Comme un effet secondaire positif, il enregistre l'échec de votre service dans le journal des événements.

Voici quelques citations de la partie pertinente de la documentation:

Si un service d'appels SetServiceStatus avec le dwCurrentState de l'ensemble des membres de SERVICE_STOPPED et la dwWin32ExitCode de l'ensemble des membres à une valeur différente de zéro, l'entrée suivante est écrit dans le journal des événements Système:

[...] <Nom_service> arrêté avec l'erreur suivante: <ExitCode> [...]

Les éléments suivants sont les meilleures pratiques lors de l'appel de cette fonction:

[...]

  • Si le statut est SERVICE_STOPPED, effectuer tout le nécessaire de nettoyage et d'appel SetServiceStatus une seule fois. Cette fonction fait appel LRPC de la SCM. Le premier appel à la fonction dans le SERVICE_STOPPED état ferme la RPC handle de contexte et des appels subséquents peuvent provoquer le processus de collision.
  • Ne tentez pas d'effectuer tout travail additionnel après l'appel de SetServiceStatus avec SERVICE_STOPPED, parce que le processus de service peut être résilié à tout moment.

PS: À mon avis, si les ressources du réseau sont pas disponibles, le service ne doit pas s'arrêter, mais continue de fonctionner, d'attente pour les ressources deviennent disponibles. Temporaire du réseau coupures de courant peuvent se produire, et ils ne devraient pas nécessiter d'intervention manuelle de l'administrateur système une fois que le réseau est de retour.

0voto

csname1910 Points 117

Vous pouvez obtenir le code Exit approprié comme décrit ici . Ainsi, le Gestionnaire de services Windows donnera le bon texte d'erreur.

Mon OnStart dans ServiceBase ressemble à ceci:

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X