Wie kann sich ein Windows-Dienst programmgesteuert neu starten?

Antworten:

187

Stellen Sie den Dienst so ein, dass er nach einem Fehler neu gestartet wird (doppelklicken Sie auf den Dienst in der Systemsteuerung und sehen Sie sich auf diesen Registerkarten um - ich habe den Namen vergessen). Wenn Sie dann möchten, dass der Dienst neu gestartet wird, rufen Sie einfach an Environment.Exit(1)(oder eine Rückgabe ungleich Null), und das Betriebssystem startet ihn für Sie neu.

TheSoftwareJedi
quelle
7
Wenn sich der Speicherort im Bedienfeld Dienste befindet, klicken Sie mit der rechten Maustaste auf den betreffenden Dienst, wählen Sie Eigenschaften aus und wählen Sie dann die Registerkarte Wiederherstellung.
James Michael Hare
20
Ich sehe, wie dies das gewünschte Verhalten erreicht, aber ein Rückkehrcode von 1 soll dem System mitteilen, dass ein Fehler aufgetreten ist. Ist das nicht eine schlechte Praxis, wenn tatsächlich kein Fehler aufgetreten ist und Sie nur Ihren Dienst neu starten wollten?
Mgttlinger
4
Dies kann programmgesteuert vom Service-Installationsprogramm im Ereignis nach der Installation durchgeführt werden, ohne dass Sie sich umsehen müssen. Was passiert, wenn sich Ihr Service auf einem Remote-Server befindet und Sie ihn mehrmals täglich installieren müssen, z. B. während des Tests?
Dean Kuga
5
@mgttlinger: Ich denke, dass ja, eine schlechte Praxis ist, wenn Ihr Dienst gesund ist, so dass es nie neu gestartet werden muss. Einige Servicearchitekturen sind jedoch nicht in unserer Reichweite und wenn sie neu gestartet werden müssen, ist dies ein Symptom dafür, dass dies nicht in Ordnung ist. Daher ist es kein Problem, einige Mindestreserven (wenn möglich) zu schließen und freizugeben und sich die Füße zu schneiden, seit sie die verlassen Ein unsachgemäß laufender Dienst kann schlechter (nutzlos) sein.
Luciano
5
@JohnLeidegren Wenn Sie ein ordnungsgemäßes Herunterfahren wünschen, können Sie das Kontrollkästchen "Aktionen für Stopps mit Fehlern aktivieren" auf dem Einstellungsbildschirm für die Dienstwiederherstellung festlegen Service.ExitCode = 1und dann ausführen Service.Stop(). Wenn Sie dies auf diese Weise tun, wird das System ordnungsgemäß heruntergefahren und der Neustart ausgelöst.
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
quelle
3
Denken Sie daran, "" um Ihren Dienstnamen herum hinzuzufügen, wenn dieser Leerzeichen enthält.
APC
4
Dies gilt nur für Dienste, die unter einem privilegierten Konto ausgeführt werden, was ohnehin eine schlechte Idee ist.
Dmitry Gusarov
17

Sie können nicht sicher sein, ob das Benutzerkonto, unter dem Ihr Dienst ausgeführt wird, überhaupt die Berechtigung hat, den Dienst zu stoppen und neu zu starten.

Greg Hewgill
quelle
Obwohl + 1-ed, da ich mit dem gleichen Problem konfrontiert war, stellt sich beim Betrachten von sc.exe heraus, dass beim Aufrufen von OpenSCManager () SERVICE_QUERY_CONFIG als dwDesiredAccess verwendet wird und anschließend OpenService () mit SERVICE_START | aufgerufen wird SERVICE_STOP zum Öffnen eines bestimmten Dienstes und es funktioniert OK (kein Problem mit dem Zugriff). Ich bin mir nicht sicher, wie dies in .NET gemacht werden kann.
n0p
Die meisten Windows-Dienste werden als System ausgeführt, daher sollte dies kein Problem sein.
Schalten Sie den
@Switch Es gibt einen großen Kreis von Diensten, die unter NETWORK SERVICE ausgeführt werden und die standardmäßig nicht einmal das Recht haben, eine eigene ausführbare Datei zu öffnen.
AgentFire
@AgentFire, korrekt, aber Standard ist Local System, nicht NETWORK SERVICE. Das lokale System verfügt von Natur aus über die höchsten Berechtigungen für das Betriebssystem - über die Berechtigungen hinaus, die Mitgliedern der lokalen Administratorgruppe zugewiesen wurden.
Wechseln Sie am
@Switch, aber die Verwendung der meisten verfügbaren Berechtigungen verstößt gegen das PoL-Prinzip .
AgentFire
12

Sie können einen Prozess erstellen, bei dem es sich um eine DOS-Eingabeaufforderung handelt, die sich selbst neu startet:

 Process process = new Process();
 process.StartInfo.FileName = "cmd";
 process.StartInfo.Arguments = "/c net stop \"servicename\" & net start \"servicename\"";
 process.Start();
VladL
quelle
Dies erbt, ohne etwas anderes zu tun, den Sicherheitskontext des aufrufenden Threads ... und solange dieser Kontext über genügend Leistung verfügt, um neu zu starten, sind Sie gut.
Clay
12
const string strCmdText = "/C net stop \"SERVICENAME\"&net start \"SERVICENAME\"";
Process.Start("CMD.exe", strCmdText);

Wo SERVICENAMEist der Name Ihres Dienstes (doppelte Anführungszeichen, um Leerzeichen im Dienstnamen zu berücksichtigen, können ansonsten weggelassen werden).

Sauber, keine automatische Neustartkonfiguration erforderlich.

Filip
quelle
9
Ich habe etwas Neues über & vs && gelernt: [Befehl1] & [Befehl2] führen beide Befehle immer nacheinander aus, während [Befehl1] && [Befehl2] Befehl2 nur ausführt, wenn Befehl1 erfolgreich ausgeführt wird ( autoitscript.com/forum/topic/… )
Jeffrey Ritter
5

Es hängt davon ab, warum Sie möchten, dass es sich selbst neu startet.

Wenn Sie nur nach einer Möglichkeit suchen, den Dienst regelmäßig selbst zu bereinigen, wird möglicherweise ein Timer im Dienst ausgeführt, der regelmäßig eine Bereinigungsroutine verursacht.

Wenn Sie nach einer Möglichkeit suchen, bei einem Fehler neu zu starten, kann der Service-Host diese Funktion beim Einrichten bereitstellen.

Warum müssen Sie den Server neu starten? Was versuchst du zu erreichen?

Brody
quelle
1
Dies würde nicht funktionieren, wenn Sie sich mit dem Heap großer Objekte und der Speicherfragmentierung befassen. Angeblich haben .NET 4 / 4.5- und 64-Bit-Prozesse dabei sehr geholfen.
David Kassa
3

Ich glaube nicht, dass Sie dies in einem eigenständigen Dienst tun können (wenn Sie Restart aufrufen, wird der Dienst gestoppt, wodurch der Befehl Restart unterbrochen wird und er wird nie wieder gestartet). Wenn Sie eine zweite EXE-Datei hinzufügen können (eine Konsolen-App, die die ServiceManager-Klasse verwendet), können Sie die eigenständige EXE-Datei starten, den Dienst neu starten und dann beenden.

Beim zweiten Gedanken könnte der Dienst wahrscheinlich eine geplante Aufgabe registrieren lassen (z. B. über den Befehlszeilenbefehl 'at'), um den Dienst zu starten und ihn dann selbst stoppen zu lassen. das würde wahrscheinlich funktionieren.

technophil
quelle
3

Das Problem beim Auslagern in eine Batchdatei oder EXE besteht darin, dass ein Dienst möglicherweise über die zum Ausführen der externen App erforderlichen Berechtigungen verfügt oder nicht.

Der sauberste Weg, dies zu tun, ist die OnStop () -Methode, die der Einstiegspunkt für den Service Control Manager ist. Dann wird Ihr gesamter Bereinigungscode ausgeführt, und Sie haben keine hängenden Sockets oder andere Prozesse, vorausgesetzt, Ihr Stoppcode erledigt seine Aufgabe.

Dazu müssen Sie vor dem Beenden ein Flag setzen, das die OnStop-Methode anweist, mit einem Fehlercode zu beenden. dann weiß der SCM, dass der Dienst neu gestartet werden muss. Ohne dieses Flag können Sie den Dienst nicht manuell vom SCM aus stoppen. Dies setzt auch voraus, dass Sie den Dienst so eingerichtet haben, dass er bei einem Fehler neu gestartet wird.

Hier ist mein Stoppcode:

...

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

Wenn der Dienst auf ein Problem stößt und neu gestartet werden muss, starte ich einen Thread, der den Dienst vom SCM stoppt. Dies ermöglicht es dem Dienst, nach sich selbst aufzuräumen:

...

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

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

Ich würde den Windows Scheduler verwenden, um einen Neustart Ihres Dienstes zu planen. Das Problem ist, dass Sie sich nicht neu starten können, aber Sie können sich selbst stoppen. (Sie haben im Wesentlichen den Ast abgesägt, auf dem Sie sitzen ... wenn Sie meine Analogie verstehen.) Sie benötigen einen separaten Prozess, um dies für Sie zu tun. Der Windows Scheduler ist geeignet. Planen Sie eine einmalige Aufgabe, um Ihren Dienst (auch innerhalb des Dienstes selbst) neu zu starten und sofort auszuführen.

Andernfalls müssen Sie einen "Hirten" -Prozess erstellen, der dies für Sie erledigt.

dviljoen
quelle
2

Die erste Antwort auf die Frage ist die einfachste Lösung: "Environment.Exit (1)" Ich verwende dies unter Windows Server 2008 R2 und es funktioniert perfekt. Der Dienst stoppt sich selbst, das Betriebssystem wartet 1 Minute und startet es dann neu.

tangentMan
quelle
Wie lange es wartet, hängt davon ab, wie lange Sie es zum Warten eingerichtet haben. Die Standardeinstellung ist zwar 1 Minute, aber wenn jemand nicht möchte, dass Ihre Antwort den falschen Eindruck vermittelt.
Espen
1

Ich glaube nicht, dass es geht. Wenn ein Dienst "gestoppt" wird, wird er vollständig entladen.

Okay, ich nehme an, es gibt immer einen Weg. Sie können beispielsweise einen getrennten Prozess erstellen, um den Dienst zu stoppen, ihn dann neu zu starten und dann zu beenden.

TED
quelle
0

Der bessere Ansatz könnte darin bestehen, den NT-Dienst als Wrapper für Ihre Anwendung zu verwenden. Wenn der NT-Dienst gestartet wird, kann Ihre Anwendung in einem "Leerlauf" -Modus gestartet werden, der auf den Start des Befehls wartet (oder so konfiguriert sein, dass er automatisch gestartet wird).

Denken Sie an ein Auto, wenn es gestartet wird, beginnt es im Leerlauf und wartet darauf, dass Ihr Befehl vorwärts oder rückwärts geht. Dies bietet auch andere Vorteile, z. B. eine bessere Remoteverwaltung, da Sie auswählen können, wie Ihre Anwendung verfügbar gemacht werden soll.

plamb
quelle
0

Gerade vorbei: und dachte, ich würde ein paar zusätzliche Informationen hinzufügen ...

Sie können auch eine Ausnahme auslösen. Dadurch wird der Windows-Dienst automatisch geschlossen, und die Optionen für den automatischen Neustart werden aktiviert. Das einzige Problem dabei ist, dass die JIT versucht, den PC einzuschalten, wenn Sie eine Entwicklungsumgebung auf Ihrem PC haben. und Sie erhalten eine Eingabeaufforderung mit der Meldung Debug J / N. Sagen Sie Nein, und dann wird es geschlossen und dann ordnungsgemäß neu gestartet. (Auf einem PC ohne JIT funktioniert einfach alles). Der Grund für das Trolling ist, dass diese JIT neu in Win 7 ist (sie funktionierte früher einwandfrei mit XP usw.) und ich versuche, eine Möglichkeit zu finden, die JIT zu deaktivieren. Ich kann die hier erwähnte Environment.Exit-Methode ausprobieren das funktioniert auch

Kristian: Bristol, Großbritannien

Kristian
quelle
3
Alle diese Lösungen mit Ausnahme von exit (1) verhindern eine ordnungsgemäße Bereinigung einer Anwendung.
Ungnädig zu beenden
0

Erstellen Sie eine restart.bat-Datei wie diese

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

Löschen Sie dann die Aufgabe% taskname%, wenn Ihr% service% gestartet wird

Erik Martino
quelle
0

Erstellen Sie eine separate Appdomain, um den Anwendungscode zu hosten. Wenn ein Neustart erforderlich ist, können wir die App-Domäne anstelle des Prozesses (Windows-Dienst) entladen und neu laden. So funktioniert der IIS-App-Pool. Sie führen die asp.net-App nicht direkt aus, sondern verwenden einen separaten Appdmain.

CreativeManix
quelle
-2

Am einfachsten ist es, eine Batch-Datei zu haben mit:

net stop net start

und fügen Sie die Datei mit dem gewünschten Zeitintervall zum Scheduler hinzu


quelle
Es funktioniert nicht, weil der Stapel zwischen beiden Befehlen gestoppt wird, da es sich um einen untergeordneten Prozess des Dienstes selbst handelt.
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
quelle