Comment un service Windows peut-il se redémarrer par programme?

Réponses:

187

Configurez le service pour qu'il redémarre après une panne (double-cliquez sur le service dans le panneau de configuration et jetez un œil sur ces onglets - j'en oublie le nom). Ensuite, chaque fois que vous souhaitez que le service redémarre, appelez simplement Environment.Exit(1)(ou tout retour différent de zéro) et le système d'exploitation le redémarrera pour vous.

TheSoftwareJedi
la source
7
Pour info, l'emplacement se trouve dans le panneau des services, cliquez avec le bouton droit sur le service en question et sélectionnez les propriétés, puis choisissez l'onglet de récupération.
James Michael Hare
20
Je vois comment cela permet d'obtenir le comportement souhaité, mais un code de retour de 1 est destiné à indiquer au système qu'il y a eu une erreur. N'est-ce pas une mauvaise pratique s'il n'y avait en fait aucune erreur et que vous vouliez simplement redémarrer votre service?
mgttlinger
4
Cela peut être fait par programme par l'installateur du service dans l'événement après l'installation, pas besoin de cliquer ... Que faire si votre service est sur un serveur distant et que vous devez l'installer plusieurs fois par jour, par exemple pendant les tests?
Dean Kuga
5
@mgttlinger: Je pense que oui, c'est une mauvaise pratique lorsque votre service est sain, donc il n'aura jamais besoin d'être redémarré. Mais certaines architectures de services sont hors de notre portée et si elles doivent être redémarrées, c'est un symptôme que ce n'est pas bien, donc pas de problème pour fermer et libérer quelques résorces minimales (si possible) et `` se couper les pieds '', depuis le départ du un service mal exécuté peut être pire (inutile).
Luciano
5
@JohnLeidegren si vous voulez un arrêt correct, vous pouvez définir Service.ExitCode = 1puis exécuter Service.Stop()en activant la case à cocher `` Activer les actions pour les arrêts avec erreurs '' sur l'écran des paramètres de récupération du service. Le faire de cette façon permet un arrêt correct et déclenche toujours le redémarrage.
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
la source
3
N'oubliez pas d'ajouter "" autour de votre nom de service s'il contient des espaces.
apc
4
Ce n'est que pour les services fonctionnant sous un compte privilégié, ce qui est de toute façon une mauvaise idée.
Dmitry Gusarov
17

Vous ne pouvez pas être sûr que le compte d'utilisateur sous lequel votre service s'exécute dispose même des autorisations pour arrêter et redémarrer le service.

Greg Hewgill
la source
Bien que +1 soit confronté au même problème, lorsque je regarde sc.exe, il s'avère qu'il utilise SERVICE_QUERY_CONFIG comme dwDesiredAccess lors de l'appel d'OpenSCManager (), puis appelle OpenService () avec SERVICE_START | SERVICE_STOP pour ouvrir un service particulier et cela fonctionne bien (pas de problème d'accès). Je ne sais pas comment cela peut être fait dans .NET.
n0p
La plupart des services Windows fonctionnent en tant que système, cela ne devrait donc pas poser de problème.
Changez le
@Switch il y a un énorme cercle de services qui sont exécutés sous NETWORK SERVICE, qui par défaut n'a même pas le droit d'ouvrir son propre exécutable.
AgentFire
@AgentFire, correct, mais la valeur par défaut est Système local, pas SERVICE RÉSEAU. Le système local a intrinsèquement le plus haut niveau de privilèges sur le système d'exploitation - dépassant ceux attribués aux membres du groupe Administrateurs local.
Changez le
@Switch mais utiliser les privilèges les plus disponibles viole le principe PoL .
AgentFire
12

Vous pouvez créer un processus qui est une invite de commande DOS qui redémarre vous-même:

 Process process = new Process();
 process.StartInfo.FileName = "cmd";
 process.StartInfo.Arguments = "/c net stop \"servicename\" & net start \"servicename\"";
 process.Start();
VladL
la source
Ceci, sans rien faire d'autre, héritera du contexte de sécurité du thread appelant ... et tant que c'est un contexte qui a assez de puissance pour redémarrer, tout va bien.
Clay
12
const string strCmdText = "/C net stop \"SERVICENAME\"&net start \"SERVICENAME\"";
Process.Start("CMD.exe", strCmdText);

SERVICENAMEest le nom de votre service (les guillemets doubles inclus pour tenir compte des espaces dans le nom du service, peuvent être omis dans le cas contraire).

Propre, aucune configuration de redémarrage automatique nécessaire.

Filip
la source
9
appris quelque chose de nouveau sur & vs &&: [command1] & [command2] exécutera toujours les deux commandes séquentiellement, tandis que [command1] && [command2] exécute command2 uniquement si command1 s'exécute avec succès ( autoitscript.com/forum/topic/… )
Jeffrey Knight
5

Cela dépendra de la raison pour laquelle vous souhaitez qu'il redémarre.

Si vous cherchez simplement un moyen de nettoyer périodiquement le service, vous pourriez avoir une minuterie en cours d'exécution dans le service qui provoque périodiquement une routine de purge.

Si vous recherchez un moyen de redémarrer en cas de panne, l'hôte de service lui-même peut fournir cette capacité lors de sa configuration.

Alors pourquoi avez-vous besoin de redémarrer le serveur? Qu'essayez-vous d'accomplir?

Brody
la source
1
Cela ne fonctionnerait pas si vous êtes préoccupé par le tas d'objets volumineux et la fragmentation de la mémoire. On suppose que les processus .NET 4 / 4.5 et 64 bits ont beaucoup aidé.
David Kassa
3

Je ne pense pas que vous puissiez dans un service autonome (lorsque vous appelez Restart, cela arrêtera le service, ce qui interrompra la commande Restart, et il ne redémarrera plus jamais). Si vous pouvez ajouter un deuxième .exe (une application de console qui utilise la classe ServiceManager), vous pouvez lancer le .exe autonome et lui faire redémarrer le service, puis quitter.

En y réfléchissant, vous pourriez probablement demander au service d'enregistrer une tâche planifiée (en utilisant la commande de ligne de commande «at», par exemple) pour démarrer le service et ensuite l'arrêter; cela fonctionnerait probablement.

technophile
la source
3

Le problème avec le bombardement vers un fichier de commandes ou un EXE est qu'un service peut ou non disposer des autorisations nécessaires pour exécuter l'application externe.

Le moyen le plus propre de faire cela que j'ai trouvé est d'utiliser la méthode OnStop (), qui est le point d'entrée du Service Control Manager. Ensuite, tout votre code de nettoyage s'exécutera et vous n'aurez plus de sockets bloqués ni d'autres processus, en supposant que votre code d'arrêt fasse son travail.

Pour ce faire, vous devez définir un indicateur avant de terminer qui indique à la méthode OnStop de quitter avec un code d'erreur; alors le SCM sait que le service doit être redémarré. Sans cet indicateur, vous ne pourrez pas arrêter le service manuellement à partir du SCM. Cela suppose également que vous avez configuré le service pour qu'il redémarre en cas d'erreur.

Voici mon code d'arrêt:

...

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 le service rencontre un problème et doit redémarrer, je lance un thread qui arrête le service du SCM. Cela permet au service de nettoyer après lui-même:

...

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

void RestartThread()
{
    ServiceController sc = new ServiceController(ServiceName);
    try
    {
        sc.Stop();
    }
    catch (Exception) { }
}
user3235770
la source
2

J'utiliserais le planificateur Windows pour planifier un redémarrage de votre service. Le problème est que vous ne pouvez pas redémarrer vous-même, mais vous pouvez vous arrêter. (Vous avez essentiellement scié la branche sur laquelle vous êtes assis ... si vous comprenez mon analogie) Vous avez besoin d'un processus séparé pour le faire pour vous. Le planificateur Windows est approprié. Planifiez une tâche unique pour redémarrer votre service (même à partir du service lui-même) à exécuter immédiatement.

Sinon, vous devrez créer un processus de "berger" qui le fera pour vous.

dviljoen
la source
2

La première réponse à la question est la solution la plus simple: "Environment.Exit (1)" J'utilise ceci sur Windows Server 2008 R2 et cela fonctionne parfaitement. Le service s'arrête, le O / S attend 1 minute, puis le redémarre.

tangentMan
la source
La durée d'attente dépend de la durée pendant laquelle vous l'avez configurée. La valeur par défaut est de 1 minute, mais si quelqu'un ne veut pas que votre réponse donne une mauvaise impression.
Espen
1

Je ne pense pas que ce soit possible. Lorsqu'un service est "arrêté", il est totalement déchargé.

Eh bien, d'accord, il y a toujours un moyen je suppose. Par exemple, vous pouvez créer un processus détaché pour arrêter le service, puis le redémarrer, puis quitter.

TED
la source
0

La meilleure approche peut être d'utiliser le service NT comme un wrapper pour votre application. Lorsque le service NT est démarré, votre application peut démarrer en mode "inactif" en attendant que la commande démarre (ou être configurée pour démarrer automatiquement).

Pensez à une voiture, lorsqu'elle a démarré, elle commence dans un état de ralenti, en attendant que votre commande avance ou recule. Cela permet également d'autres avantages, tels qu'une meilleure administration à distance, car vous pouvez choisir comment exposer votre application.

plamb
la source
0

Juste en passant: et j'ai pensé ajouter quelques informations supplémentaires ...

vous pouvez également lancer une exception, cela fermera automatiquement le service Windows, et les options de redémarrage automatique se déclenchent. Le seul problème avec ceci est que si vous avez un environnement de développement sur votre PC, le JIT essaie de démarrer, et vous obtiendrez une invite indiquant le débogage O / N. dites non, puis il se fermera, puis redémarrera correctement. (sur un PC sans JIT, tout fonctionne). la raison pour laquelle je suis à la traîne, est que ce JIT est nouveau dans Win 7 (il fonctionnait bien avec XP, etc.) et que je tente de trouver un moyen de désactiver le JIT .... je peux essayer la méthode Environment.Exit mentionnée ici voir comment cela fonctionne aussi.

Kristian: Bristol, Royaume-Uni

Kristian
la source
3
Toutes ces solutions d'exception, exit (1) évitent toutes le nettoyage correct d'une application. Sortir sans grâce est une mauvaise solution
devshorts
0

Créez un fichier restart.bat comme celui-ci

@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%

Puis supprimez la tâche% taskname% lorsque votre% service% démarre

Erik Martino
la source
0

Créez un domaine d'application distinct pour héberger le code d'application. Lorsque le redémarrage est nécessaire, nous pourrions décharger et recharger le domaine d'application à la place du processus (service Windows). C'est ainsi que fonctionne le pool d'applications IIS, ils n'exécutent pas l'application asp.net directement, ils utilisent appdmain séparé.

CreativeManix
la source
-2

Le moyen le plus simple est d'avoir un fichier batch avec:

net stop net start

et ajoutez le fichier au planificateur avec l'intervalle de temps souhaité


la source
Cela ne fonctionne pas car le lot est arrêté entre les deux commandes, car il s'agit d'un processus enfant du service lui-même.
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
la source