Come può un servizio Windows riavviarsi a livello di codice?


Risposte:


187

Imposta il riavvio del servizio dopo un errore (fai doppio clic sul servizio nel pannello di controllo e dai un'occhiata in quelle schede - Ho dimenticato il nome). Quindi, ogni volta che vuoi riavviare il servizio, chiama Environment.Exit(1)(o qualsiasi ritorno diverso da zero) e il sistema operativo lo riavvierà per te.


7
Fyi la posizione è nel pannello dei servizi, fai clic con il pulsante destro del mouse sul servizio in questione e seleziona le proprietà, quindi scegli la scheda di ripristino.
James Michael Hare,

20
Vedo come questo raggiunge il comportamento desiderato, ma un codice di ritorno di 1 ha lo scopo di dire al sistema che si è verificato un errore. Non è una cattiva pratica se in realtà non si sono verificati errori e si desidera semplicemente riavviare il servizio?
mgttlinger,

4
Questo può essere fatto a livello di codice dal programma di installazione del servizio nell'evento post-installazione, non è necessario fare clic su ... Cosa succede se il servizio è su un server remoto e è necessario installarlo più volte al giorno, ad esempio durante i test?
Dean Kuga,

5
@mgttlinger: Penso che sì, è una cattiva pratica quando il tuo servizio è sano, quindi non dovrà mai essere riavviato. Ma alcuni servizi di architettura sono fuori dalla nostra portata e se devono essere riavviati è un sintomo che non va bene, quindi nessun problema a chiudere e liberare alcune risorse minime (se possibile) e 'tagliare i piedi', dal momento che lasciando il il servizio eseguito in modo improprio può essere peggio (inutile).
Luciano,

5
@JohnLeidegren se si desidera un arresto corretto, è possibile impostare Service.ExitCode = 1e quindi eseguire Service.Stop()insieme con la casella di controllo "Abilita azioni per fermate con errori" nella schermata delle impostazioni di ripristino del servizio. In questo modo consente un arresto corretto e innesca ancora il riavvio.
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()

3
Ricorda di aggiungere "" intorno al nome del servizio se contiene spazi.
aprile

4
Questo è solo per i servizi in esecuzione con account privilegiato, che è comunque una cattiva idea.
Dmitry Gusarov,

17

Non puoi essere sicuro che l'account utente su cui è in esecuzione il tuo servizio disponga anche delle autorizzazioni per arrestare e riavviare il servizio.


Anche se ho affrontato lo stesso problema con + 1-ed, osservando sc.exe si scopre che utilizza SERVICE_QUERY_CONFIG come dwDesiredAccess quando si chiama OpenSCManager () e quindi chiama OpenService () con SERVICE_START | SERVICE_STOP per aprire un particolare servizio e funziona correttamente (nessun problema con l'accesso). Non sono sicuro di come sia possibile farlo in .NET.
n0p,

La maggior parte dei servizi Windows funziona come Sistema, quindi non dovrebbe essere un problema.
Cambia il

@Switch c'è una vasta cerchia di servizi che vengono eseguiti sotto SERVIZIO DI RETE, che per impostazione predefinita non ha nemmeno i diritti per aprire il proprio eseguibile.
AgentFire,

@AgentFire, corretto, ma l'impostazione predefinita è Sistema locale, non SERVIZIO DI RETE. Il sistema locale ha intrinsecamente il più alto livello di privilegi per il sistema operativo, superando quelli assegnati ai membri del gruppo di amministratori locali.
Passa dal

@Switch ma l'utilizzo dei privilegi più disponibili viola il Principio PoL .
AgentFire

12

È possibile creare un processo che è un prompt dei comandi DOS che si riavvia:

 Process process = new Process();
 process.StartInfo.FileName = "cmd";
 process.StartInfo.Arguments = "/c net stop \"servicename\" & net start \"servicename\"";
 process.Start();

Questo, senza fare altro, erediterà il contesto di sicurezza del thread chiamante ... e fintanto che è un contesto che ha abbastanza potenza per riavviarsi, sei bravo.
Clay

12
const string strCmdText = "/C net stop \"SERVICENAME\"&net start \"SERVICENAME\"";
Process.Start("CMD.exe", strCmdText);

dov'è SERVICENAMEil nome del tuo servizio (le doppie virgolette incluse per tenere conto degli spazi nel nome del servizio, possono essere omesse in altro modo).

Pulito, nessuna configurazione di riavvio automatico necessaria.


9
imparato qualcosa di nuovo su & vs &&: [command1] & [command2] eseguirà sempre entrambi i comandi in sequenza, mentre [command1] && [command2] esegue command2 solo se command1 viene eseguito correttamente ( autoitscript.com/forum/topic/… )
Jeffrey Knight,

5

Dipenderebbe dal motivo per cui si desidera che si riavvii da solo.

Se stai solo cercando un modo per fare in modo che il servizio si pulisca periodicamente, allora potresti avere un timer in esecuzione nel servizio che provoca periodicamente una routine di spurgo.

Se stai cercando un modo per riavviare in caso di errore, l'host del servizio stesso può fornire tale capacità durante l'installazione.

Quindi perché è necessario riavviare il server? Cosa stai cercando di ottenere?


1
Questo non funzionerebbe se ti preoccupi dell'heap di oggetti di grandi dimensioni e della frammentazione della memoria. Presumibilmente i processi .NET 4 / 4.5 e 64-bit hanno aiutato molto in questo.
David Kassa,

3

Non penso che tu possa farlo in un servizio autonomo (quando chiami Riavvia, il servizio verrà interrotto, interrompendo il comando Riavvia e non verrà mai riavviato). Se puoi aggiungere un secondo .exe (un'app Console che utilizza la classe ServiceManager), puoi dare il via al file .exe autonomo e farlo riavviare il servizio, quindi uscire.

Ripensandoci, è possibile che il servizio registri un'attività pianificata (ad esempio utilizzando il comando "at" della riga di comando) per avviare il servizio e farlo arrestare automaticamente; probabilmente funzionerebbe.


3

Il problema con lo sbandamento verso un file batch o EXE è che un servizio può o meno disporre delle autorizzazioni necessarie per eseguire l'app esterna.

Il modo più pulito per fare ciò che ho trovato è utilizzare il metodo OnStop (), che è il punto di ingresso per Gestione controllo servizi. Quindi verrà eseguito tutto il codice di pulizia e non avrai socket sospesi o altri processi, supponendo che il codice di arresto stia facendo il suo lavoro.

Per fare ciò è necessario impostare un flag prima di terminare che indica al metodo OnStop di uscire con un codice di errore; quindi SCM sa che il servizio deve essere riavviato. Senza questo flag non sarà possibile interrompere manualmente il servizio da SCM. Ciò presuppone inoltre che il servizio sia stato impostato per il riavvio in caso di errore.

Ecco il mio codice di stop:

...

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

Se il servizio presenta un problema e deve essere riavviato, avvio un thread che interrompe il servizio da SCM. Ciò consente al servizio di ripulire dopo se stesso:

...

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

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

2

Vorrei utilizzare l'Utilità di pianificazione di Windows per pianificare un riavvio del servizio. Il problema è che non puoi riavviarti, ma puoi fermarti. (In sostanza hai segato il ramo su cui sei seduto ... se ottieni la mia analogia) Hai bisogno di un processo separato per farlo per te. L'utilità di pianificazione di Windows è appropriata. Pianifica un'attività una tantum per riavviare il servizio (anche dall'interno del servizio stesso) per l'esecuzione immediata.

Altrimenti, dovrai creare un processo di "pastorizia" che lo faccia per te.


2

La prima risposta alla domanda è la soluzione più semplice: "Environment.Exit (1)" Sto usando questo su Windows Server 2008 R2 e funziona perfettamente. Il servizio si arresta automaticamente, l'O / S attende 1 minuto, quindi lo riavvia.


Il tempo di attesa dipende dal tempo di impostazione. L'impostazione predefinita è 1 minuto, ma se qualcuno non desidera che la tua risposta dia l'impressione sbagliata.
Espen,

1

Non penso che possa. Quando un servizio viene "arrestato", viene scaricato completamente.

Bene, ok, c'è sempre un modo per supporre. Ad esempio, è possibile creare un processo distaccato per interrompere il servizio, quindi riavviarlo, quindi uscire.


0

L'approccio migliore potrebbe essere quello di utilizzare il servizio NT come wrapper per l'applicazione. All'avvio del servizio NT, l'applicazione può avviarsi in modalità "inattiva" in attesa dell'avvio del comando (o essere configurata per l'avvio automatico).

Pensa a un'auto, quando è avviata inizia in uno stato inattivo, in attesa che il tuo comando vada avanti o indietro. Ciò consente anche altri vantaggi, come una migliore amministrazione remota in quanto è possibile scegliere come esporre l'applicazione.


0

Solo passando: e ho pensato di aggiungere alcune informazioni extra ...

puoi anche lanciare un'eccezione, questo chiuderà automaticamente il servizio di Windows e le opzioni di riavvio automatico si avvieranno. L'unico problema è che se hai un ambiente di sviluppo sul tuo PC, allora la JIT prova a dare il via, e riceverai un prompt che dice debug S / N. dire di no e poi si chiuderà, quindi ricominciare correttamente. (su un PC senza JIT funziona solo tutto). il motivo per cui sto trollando, è che JIT è nuovo su Win 7 (funzionava bene con XP ecc.) e sto cercando di trovare un modo per disabilitare JIT .... potrei provare il metodo Environment.Exit menzionato qui, vedere come anche quello funziona.

Kristian: Bristol, Regno Unito


3
Tutte queste soluzioni di eccezione, exit (1) evitano tutte la corretta pulizia di un'applicazione.
Uscire in modo non

0

Crea un file restart.bat come questo

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

Quindi eliminare l'attività% taskname% all'avvio di% service%


0

Crea un appdomain separato per ospitare il codice dell'applicazione. Quando è necessario il riavvio, è possibile scaricare e ricaricare l'appdomain anziché il processo (servizio Windows). Ecco come funziona il pool di app IIS, non eseguono direttamente l'app asp.net, usano appdmain separato.


-2

Il modo più semplice è avere un file batch con:

net stop net start

e aggiungi il file allo scheduler con l'intervallo di tempo desiderato


Non funziona perché il batch viene arrestato tra entrambi i comandi, poiché è un processo figlio del servizio stesso.
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();
        }
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.