Windows-Dienste erstellen und betreiben ── Von der Abgrenzung zur Aufgabenplanung bis zur Umwandlung eines BackgroundService in einen Dienst
· Go Komura · Windows-Dienst, Windows, .NET, C#, BackgroundService, Generic Host, Hintergrundverarbeitung, Betrieb, Entscheidungstabelle, Technische Beratung
„Die Aufgabenplanung alle 5 Minuten reicht nicht mehr aus.“ „Wir wollen einen Überwachungsdienst dauerhaft laufen lassen, der ständig auf Daten von einem Gerät wartet.“ „Sobald eine Datei in einem Ordner abgelegt wird, soll sie innerhalb weniger Sekunden verarbeitet werden.“ Wer regelmäßig zu wiederkehrenden Ausführungen berät, landet, sobald die Anforderungen wachsen, zwangsläufig bei genau diesem Thema.
In diesem Blog haben wir bereits fortlaufend über Automatisierung geschrieben: Geschäftsprozessautomatisierung mit Power Automate und Zuverlässiger Betrieb der Aufgabenplanung. Im 8. Kapitel des Artikels zur Aufgabenplanung haben wir die Grenze gezogen: „Sobald minutengenaues Polling oder ständige Überwachung nötig wird, betreten Sie das Terrain residenter Prozesse.“ Aber wie baut man tatsächlich einen Windows-Dienst, und wie bringt man ihn zuverlässig in den Produktivbetrieb? Bleibt das im Vagen und macht man „erst mal einen Dienst daraus“, entstehen Dienste, die sich nicht stoppen lassen, Dienste, die abstürzen, ohne dass es jemand bemerkt, und Dienste, die unter LocalSystem alles Mögliche tun dürfen.
Dieser Artikel ordnet, in der Reihenfolge, in der Sie das in der Praxis tatsächlich entscheiden: eine Entscheidungstabelle, ob ein residenter Prozess zu einem Windows-Dienst werden sollte, das nötige Mindestwissen über die Funktionsweise von Diensten (SCM = Dienststeuerungs-Manager, Starttyp, Sitzung 0), eine Implementierung mit dem .NET-8-Worker-Service, die Registrierung und Wiederherstellungsoptionen mit sc.exe, die Wahl des Ausführungskontos und schließlich ein sicheres Beenden.
1. Das Wichtigste zuerst
- Die Entscheidungsachse ── Bei periodischen Aufgaben im Abstand von mehreren Minuten oder mehr reicht die Aufgabenplanung. Sobald ständiges Warten (Sockets, benannte Pipes, FileSystemWatcher), eine Reaktion im Sekundenbereich oder eine automatische Wiederherstellung nach einem Absturz zu den Anforderungen gehören, brauchen Sie einen Windows-Dienst.
- Keine Benutzeroberfläche möglich ── Seit Windows Vista laufen Dienste in Sitzung 0 und können nicht direkt mit dem Benutzer interagieren (keine UI anzeigen). Wird eine UI benötigt, trennen Sie Dienst und UI-Anwendung in zwei Prozesse, die über Interprozesskommunikation (IPC) miteinander sprechen.1
- Der Implementierungsweg ── Der offizielle Weg für .NET ist das Worker-Service-Template zusammen mit
AddWindowsServiceausMicrosoft.Extensions.Hosting.WindowsServices(bei derIHostBuilder-FamilieUseWindowsService). Die Anwendung lässt sich unverändert als Konsolenanwendung ausführen, was Entwicklung und Debugging gegenüber der klassischen Dienstentwicklung erheblich erleichtert.2 - Ausnahmen und Neustart ── Ab .NET 6 stoppt eine unbehandelte Ausnahme innerhalb von
BackgroundServicestandardmäßig den Host (BackgroundServiceExceptionBehavior.StopHost). Da dies jedoch als „normales Beenden“ gilt, greift der Neustart über die Wiederherstellungsoptionen des SCM (Dienststeuerungs-Manager) nicht. Bei einem Fehler, der einen Neustart auslösen soll, beenden Sie den Prozess mit einem Exit-Code ungleich 0.32 - Das Ausführungskonto ── Wählen Sie nicht aus Trägheit LocalSystem. Da
sc.exe createstandardmäßig LocalSystem verwendet4, läuft der Dienst unbemerkt mit maximalen Rechten, wenn Sie nicht bewusst etwas anderes wählen. Für rein lokale Aufgaben sind LocalService oder ein virtuelles Konto die richtige Wahl, für den Zugriff auf Freigaben oder Datenbanken in einer Domäne ist gMSA (Group Managed Service Account, ein Dienstkonto, dessen Passwort die Domäne automatisch verwaltet) die erste Wahl.5 - Wiederherstellung und Beenden ── Wiederherstellungsoptionen (
sc.exe failure) und das Beenden (HostOptions.ShutdownTimeout, standardmäßig 30 Sekunden6) legen Sie bei der Registrierung fest. Ein „Dienst, der nicht auf Stoppanforderungen reagiert“, ist im Betrieb das meistgehasste Ärgernis.
2. Wann sollte etwas ein Dienst werden? ── Entscheidungstabelle für Aufgabenplanung, Dienst und residente Anwendung
Wenn eine Anforderung auftaucht, die nach etwas Dauerhaftem klingt, gibt es drei Optionen: die Aufgabenplanung, einen Windows-Dienst oder eine „beim Start registrierte residente Anwendung“ (die Art, die im Infobereich residiert). Stellen wir ihre Eigenschaften gegenüber.
| Aspekt | Aufgabenplanung | Windows-Dienst | Residente Anwendung (Autostart-Registrierung) |
|---|---|---|---|
| Startzeitpunkt | Bei Bedarf, ausgelöst durch Uhrzeit oder Ereignis | Ab OS-Start (vor der Anmeldung) dauerhaft aktiv | Ab der Benutzeranmeldung |
| Anmeldung erforderlich? | Kann ohne Anmeldung laufen (nicht interaktiv) | Nicht erforderlich | Erforderlich (verschwindet bei Abmeldung) |
| UI | Nicht möglich (in nicht interaktiver Konfiguration) | Nicht möglich (Sitzung 0) | Möglich (Infobereich, Dialoge) |
| Dauerhaft oder periodisch | Periodisch (geeignet für stündlich bis täglich) | Dauerhaft | Dauerhaft (aber innerhalb der Benutzersitzung) |
| Rechte | Ausführungskonto pro Aufgabe festgelegt | Dienstkonto (Kapitel 6) | Rechte des angemeldeten Benutzers |
| Überwachung/Wiederherstellung | Verlauf + eigene Benachrichtigung | Wiederherstellungsoptionen des SCM (automatischer Neustart) | Keine (muss selbst gebaut werden) |
| Bereitstellungsaufwand | Registrierung über XML/PowerShell | sc.exe / Installationsprogramm | Registrierung z. B. im Run-Schlüssel |
Die Entscheidungsachse sieht so aus:
- Bei periodischen Aufgaben – von mehrmals täglich bis stündlich –, die zwischen den Durchläufen keinen Zustand halten, verwenden Sie die Aufgabenplanung. Das eigens dafür zu einem Dienst zu machen und eine eigene Timer-Verwaltung zu bauen, ist überzogen; das im Artikel zur Aufgabenplanung beschriebene Betriebsdesign ist deutlich günstiger.
- Wenn ständiges Warten der Kern der Aufgabe ist – Anfragen über TCP oder eine Pipe entgegennehmen, eine Ordnerüberwachung mit FileSystemWatcher, das sequenzielle Abarbeiten einer Warteschlange, eine Reaktion im Sekundenbereich –, ist das ein Fall für einen Windows-Dienst. Wenn Sie anfangen, die Arbeit in eine „Aufgabe alle 5 Minuten“ zu zerlegen und in Häppchen zu pollen, ist das ein Zeichen dafür, dass Sie einen residenten Prozess schlecht nachbauen.
- Ist die Benutzeroberfläche der eigentliche Zweck (Bedienung über das Infobereichssymbol, Popups an den Benutzer), ist das eine residente Anwendung. Sie läuft allerdings nur, solange jemand angemeldet ist, und eignet sich daher nicht für serverartige Zwecke. Wenn „die Hintergrundverarbeitung dauerhaft laufen muss, aber auch eine UI gebraucht wird“, teilen Sie sie wie im nächsten Kapitel beschrieben in zwei Prozesse auf: einen Dienst und eine UI-Anwendung.
Ein weiteres, leicht übersehenes Entscheidungskriterium ist die Wiederherstellung. Ein fehlgeschlagener Auftrag der Aufgabenplanung bleibt bis zum nächsten geplanten Lauf einfach liegen, während sich bei einem Dienst der SCM um den automatischen Neustart kümmert (Kapitel 5). Eine Anforderung wie „wenn er nachts abstürzt, soll er sich bis zum Morgen von selbst erholt haben“ ist für sich genommen bereits ein Grund, daraus einen Dienst zu machen.
3. Das nötige Mindestwissen über die Funktionsweise von Diensten ── SCM, Starttyp, Sitzung 0
3.1 SCM und Starttyp
Der eigentliche Betreiber eines Windows-Dienstes ist der Dienststeuerungs-Manager (Service Control Manager, SCM). Er verwaltet das Register der Dienste, vermittelt Start- und Stoppanforderungen und führt bei einem Fehler die Wiederherstellungsaktion aus. Ein Dienstprozess muss so gebaut sein, dass er mit dem SCM gemäß dessen Protokoll kommunizieren kann, aber diesen Teil übernehmen die .NET-Bibliotheken für uns (Kapitel 4). Was wir als Entwurfsentscheidung selbst treffen, sind vier Punkte: Starttyp, Ausführungskonto, Wiederherstellung und Beenden.
Für den Starttyp gibt es vier Möglichkeiten.4
| Starttyp | Verhalten | Einsatzbereich |
|---|---|---|
| Automatisch (auto) | Startet beim OS-Start | Grundlage für dauerhaft aktive Dienste |
| Automatisch (verzögerter Start) (delayed-auto) | Startet etwas später als andere automatische Dienste | Erste Wahl für Fachanwendungsdienste. Vermeidet Überlastung direkt nach dem Start und noch nicht bereitstehende Abhängigkeiten |
| Manuell (demand) | Startet nur auf Anforderung | Hilfsdienste, die von anderen Anwendungen gestartet werden |
| Deaktiviert (disabled) | Kann nicht gestartet werden | Außerbetriebnahme / Sperrung |
Machen Sie den verzögerten Start für Fachanwendungsdienste zur Regel. Direkt nach dem OS-Start sind Netzwerk, Datenbank und andere Dienste noch nicht vollständig verfügbar, sodass ein Start mit „Automatisch“ so schnell wie möglich beim ersten Verbindungsversuch häufig fehlschlägt. Stabil ist ein zweistufiges Vorgehen: mit dem verzögerten Start Zeit gewinnen und verbleibende Verbindungsfehler anschließend über Wiederholungsversuche innerhalb von ExecuteAsync (siehe unten) abfangen.
Eine weitere Sache sollten Sie kennen: das Start-Timeout. Der SCM wartet nicht unbegrenzt darauf, dass ein Dienst den Abschluss seines Starts meldet. Wird das Standard-Timeout (ServicesPipeTimeout, 30 Sekunden) überschritten, werden die Ereignisse 7000/7011 protokolliert, und der Start gilt als fehlgeschlagen.7 Mit anderen Worten: Führen Sie keine aufwendige Initialisierung – etwa Wiederholungsversuche für eine Datenbankverbindung oder den Aufbau eines großen Caches – innerhalb des „Start“-Schritts durch. Die eiserne Regel lautet: Den Start sofort abschließen und die aufwendige Arbeit in der Hauptschleife (ExecuteAsync) erledigen.
Fasst man die Kommunikation zwischen SCM und Dienstprozess in einem Bild zusammen, ergibt sich folgender Ablauf. Sowohl Start als auch Stopp sind ein Hin und Her aus „Anforderung“ und „Abschlussmeldung“, und dass dieses Hin und Her einem Zeitlimit unterliegt, ist ein Kennzeichen von Diensten.
sequenceDiagram
participant Adm as Administrator / OS-Start
participant SCM as SCM
participant Svc as Dienstprozess
participant BG as BackgroundService
Adm->>SCM: Startanforderung sc.exe start oder automatischer Start
SCM->>Svc: Prozess starten
Svc-->>SCM: Meldet "wird gestartet" START_PENDING
Note over SCM,Svc: Wird innerhalb des standardmäßigen ServicesPipeTimeout<br/>von 30 Sekunden kein "läuft" gemeldet, werden Ereignis 7000 / 7011 protokolliert
Svc-->>SCM: Meldet "läuft" SERVICE_RUNNING
Svc->>BG: Startet ExecuteAsync
BG-->>BG: Warten und Verarbeitungsschleife
Adm->>SCM: Stoppanforderung sc.exe stop oder Herunterfahren
SCM->>Svc: Benachrichtigt über Stoppanforderung
Svc->>BG: Bricht stoppingToken ab
BG-->>Svc: Verlässt die Schleife, StopAsync ist abgeschlossen
Note over Svc,BG: Wird das standardmäßige ShutdownTimeout von 30 Sekunden überschritten,<br/>wird das Stoppen erzwungen, ohne auf den Abschluss der Nachbereitung zu warten
Svc-->>SCM: Meldet "gestoppt" SERVICE_STOPPED
Dieses Hin und Her zu implementieren ist eigentlich Aufgabe der Dienstentwicklung, aber in .NET übernimmt das die von AddWindowsService eingebrachte Dienstlebensdauer stellvertretend (Kapitel 4). Was wir selbst schreiben, ist nur der Inhalt von ExecuteAsync und die Reaktion auf die Stoppanforderung (Kapitel 7).
3.2 Sitzung-0-Trennung ── Ein Dienst kann keine UI anzeigen
Seit Windows Vista laufen Dienste in einer isolierten Sitzung namens Sitzung 0 und können nicht direkt mit dem Benutzer interagieren.1 Ruft man innerhalb eines Dienstes MessageBox.Show auf oder zeigt ein Formular an, erscheint auf dem Bildschirm des angemeldeten Benutzers nichts. Schlimmer noch: Das ist die klassische Ursache eines Hängers, bei dem die gesamte Verarbeitung stehen bleibt, weil sie endlos auf eine OK-Schaltfläche wartet, die niemand klicken kann. Beim Portieren von altem Code in einen Dienst sollten Sie unbedingt prüfen, ob nicht noch eine als Fehleranzeige gedachte Meldungsbox übrig geblieben ist.
Warum die Sitzungen getrennt sind, in welche Sitzung man bei einer RDP-Verbindung gelangt, ob benannte Objekte sitzungsübergreifend funktionieren – die Mechanik der Sitzungstrennung selbst behandeln wir in „Wie man die Windows-Sitzungstrennung versteht“. Für diesen Artikel brauchen Sie nur den einen Punkt „ein Dienst kann keine UI anzeigen“, lesen Sie also auf dieser Grundlage weiter.
Wird eine Benutzeroberfläche benötigt, ist die richtige Lösung, Dienst (Sitzung 0) und UI-Anwendung (Benutzersitzung) in getrennte Prozesse aufzuteilen und über Interprozesskommunikation (IPC) kommunizieren zu lassen. Dies ist der von Microsoft selbst empfohlene Aufbau: Über einen IPC-Mechanismus wie eine benannte Pipe meldet die UI-Seite das Ergebnis an den Dienst zurück.1 Welchen IPC-Mechanismus Sie wählen sollten, ordnet der am selben Tag veröffentlichte Schwesterartikel „Entscheidungstabelle für Interprozesskommunikation“ ein. Setzen Sie auch die UI-Anwendung auf den Generic Host, können Sie DI, Protokollierung und Konfiguration in beiden Prozessen einheitlich aufbauen (siehe „Generic Host und BackgroundService in Desktopanwendungen einsetzen“).
4. So bauen Sie einen Dienst mit .NET ── Worker Service und AddWindowsService
4.1 Das Template und Program.cs
Die Dienstentwicklung mit .NET beginnt mit dem Worker-Service-Template. Es liegt dem SDK bei, sodass dotnet new worker das Grundgerüst erstellt; fügen Sie das Paket Microsoft.Extensions.Hosting.WindowsServices hinzu und rufen Sie AddWindowsService auf, und die Anwendung ist bereit, als Windows-Dienst zu laufen.2 Der zugrunde liegende Generic-Host-Mechanismus wird in „Was ist der .NET Generic Host?“ erläutert.
using App.MonitorService;
using Microsoft.Extensions.Hosting;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
// Bindet die Lebensdauer ein, die beim Start als Windows-Dienst mit dem SCM kommuniziert.
// Wird die Anwendung als Konsole gestartet, läuft sie einfach mit der gewöhnlichen ConsoleLifetime weiter
builder.Services.AddWindowsService(options =>
{
options.ServiceName = "KsMonitor";
});
builder.Services.AddSingleton<MeasurementQueue>();
builder.Services.AddHostedService<MonitorWorker>();
IHost host = builder.Build();
host.Run();
Bei einem älteren Projektstil mit Host.CreateDefaultBuilder rufen Sie stattdessen die IHostBuilder-Erweiterung UseWindowsService() auf. Die Rolle ist dieselbe.
Es gibt eine Falle, die dieselbe Form hat wie das Problem „Starten in (optional)“ aus dem Artikel zur Aufgabenplanung: Beim Start als Dienst ist das aktuelle Verzeichnis C:\Windows\System32. Laden Sie appsettings.json und Ähnliches unter der Annahme eines relativen Pfads, erhalten Sie „läuft in der Konsole, aber der Dienst kann die Einstellungen nicht lesen“. Lösen Sie Konfigurationsdateien relativ zur ausführbaren Datei auf, oder übergeben Sie, wie es das offizielle Tutorial empfiehlt, bei der Registrierung --contentRoot in binpath, um das Inhaltsstammverzeichnis explizit anzugeben.2
4.2 ExecuteAsync ── Entwurf rund um stoppingToken und Ausnahmen
Die Hauptlogik schreiben Sie in ExecuteAsync, überschrieben von BackgroundService. Was Sie hier entscheiden müssen, läuft auf zwei Dinge hinaus: wie auf eine Stoppanforderung reagiert wird und wie mit Ausnahmen umgegangen wird.
namespace App.MonitorService;
public sealed class MonitorWorker(
MeasurementQueue queue,
ILogger<MonitorWorker> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
// Ein Element aus der Warteschlange entnehmen und verarbeiten. Auch beim Warten immer das Token übergeben
var item = await queue.DequeueAsync(stoppingToken);
await ProcessAsync(item, stoppingToken);
}
catch (Exception ex) when (ex is IOException or TimeoutException)
{
// Ein Fehler, von dem man sich erholen kann: protokollieren und nach einer Pause erneut versuchen
logger.LogError(ex, "Verarbeitung fehlgeschlagen. Erneuter Versuch in 30 Sekunden.");
await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
}
}
}
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
{
// Eine Stoppanforderung vom SCM. Das ist der normale Ablauf, also nichts tun.
// Die when-Klausel grenzt dies bewusst auf eine durch stoppingToken ausgelöste
// Abbruchanforderung ein, damit eine von einem internen Timeout o. Ä. geworfene
// Abbruchanforderung nicht fälschlich als "normales Beenden" gilt – wodurch der
// Worker still stehen bliebe, während der Host weiterläuft
}
catch (Exception ex)
{
// Ein unerwarteter Fehler: Beim Standardverhalten StopHost beendet sich der Host
// "normal", sodass die Wiederherstellungsoption des SCM (automatischer Neustart)
// nicht auslöst. Stattdessen mit einem Exit-Code ungleich 0 abstürzen,
// um dem SCM mitzuteilen, dass es tatsächlich fehlgeschlagen ist
logger.LogCritical(ex, "Dienst wird wegen eines nicht behebbaren Fehlers beendet.");
Environment.Exit(1);
}
}
private Task ProcessAsync(Measurement item, CancellationToken token)
=> throw new NotImplementedException();
}
- Übergeben Sie
stoppingTokenan jedes Warten.Task.Delay, I/O, Warteschlangen-Waits – alle. Schon eine einzige lange Wartezeit, die das Token ignoriert, wird zum Nährboden für einen „Dienst, der nicht auf Stoppanforderungen reagiert“ (Kapitel 7). - Trennen Sie fortsetzbare von nicht behebbaren Fehlern. Fangen Sie Netzwerkabbrüche und vorübergehende Timeouts innerhalb der Schleife ab, protokollieren Sie sie und versuchen Sie es erneut. Ein
catch (Exception), das den Fehler einfach schluckt und mitcontinueweitermacht, erzeugt nur einen Dienst, der still im Leerlauf dreht. Wie Sie entscheiden, in welche Kategorie ein bestimmter Fehler fällt, ordnet „Entscheidungstabelle: Bei einer unerwarteten Ausnahme beenden oder fortsetzen“ ein. - Kennen Sie das Standardverhalten bei einer unbehandelten Ausnahme. Vor .NET 6 verschwand eine aus
ExecuteAsyncentweichende Ausnahme spurlos, und der Dienst wurde zu einem „Zombie“, der lief, aber nichts mehr tat. Ab .NET 6 ist der StandardBackgroundServiceExceptionBehavior.StopHost: Die Ausnahme wird protokolliert, und anschließend stoppt der Host.3 Dieser Stopp gilt jedoch als „normales Beenden“, sodass er auch bei konfigurierten Wiederherstellungsoptionen nicht neu gestartet wird. Für Fehler, die über die Wiederherstellungsoption zurückgeholt werden sollen, beenden Sie den Prozess explizit und abnormal mitEnvironment.Exit(1), wie im obigen Code – das ist die im offiziellen Tutorial beschriebene Vorgehensweise.2
4.3 Läuft weiterhin als Konsolenanwendung ── Der größte Grund, warum die Entwicklung leichter geworden ist
AddWindowsService wechselt nur dann in die Dienstlebensdauer, wenn die Anwendung tatsächlich als Windows-Dienst gestartet wird. Das heißt, dieselbe exe läuft unter Visual Studios F5 oder dotnet run als ganz normale Konsolenanwendung. Haltepunkte funktionieren weiterhin, und mit Strg+C lässt sich der gesamte Ablauf von der Stoppanforderung bis zu StopAsync lokal überprüfen.
Dass etwas in der Konsole funktioniert, garantiert allerdings nicht, dass es auch als Dienst funktioniert. Drei Punkte – der Unterschied im Ausführungskonto (Kapitel 6), Sitzung 0 und das aktuelle Verzeichnis – müssen tatsächlich als Dienst auf echter Hardware überprüft werden. Fast jeder Fall von „funktioniert in der Konsole, aber nicht als Dienst“ lässt sich auf einen dieser drei Punkte zurückführen.
5. Registrierung und Betrieb ── sc.exe, Wiederherstellungsoptionen und Ereignisprotokollierung
5.1 Registrierung mit sc.exe create
Veröffentlichen Sie die exe (dotnet publish – eine Einzeldatei-Veröffentlichung lässt sich am leichtesten handhaben) und registrieren Sie sie anschließend mit sc.exe create beim SCM. Führen Sie dies in einer administrativen PowerShell aus.
sc.exe create "KsMonitor" binpath= "C:\Services\KsMonitor\KsMonitor.exe" start= delayed-auto obj= "NT AUTHORITY\LocalService" displayname= "KS Dienst zur Erfassung von Messdaten"
sc.exe description "KsMonitor" "Residenter Dienst, der Messdaten erfasst und in die Datenbank einträgt (zuständig: IT-Abteilung)"
Zuerst die klassische Falle: Nach dem Gleichheitszeichen von binpath= oder start= muss ein Leerzeichen stehen. Das ist eine Eigenheit von sc.exe: Alles bis zum Gleichheitszeichen ist der Optionsname, und ein Leerzeichen zwischen ihm und dem Wert ist syntaktisch zwingend erforderlich (lässt man es weg, schlägt der Befehl fehl).4 Lässt man außerdem obj= weg, ist der Standard LocalSystem.4 Registrieren Sie ohne nachzudenken, läuft der Dienst mit maximalen Rechten – treffen Sie die Wahl aus Kapitel 6 daher bereits bei der Registrierung.
Das Setzen von description ist unauffällig, aber wichtig. Wer Jahre später services.msc öffnet, kann anhand dieser Information beurteilen, „was das für ein Dienst ist, ob man ihn anhalten darf und wen man fragen sollte“ – das erspart spätere Nachforschungskosten. Zum Entfernen stoppen Sie den Dienst zuerst und führen dann sc.exe delete "KsMonitor" aus.2
Gibt es mehrere Zielrechner oder finden Aktualisierungen (Dateiaustausch) regelmäßig statt, wechseln Sie frühzeitig von der manuellen Arbeit mit sc.exe zu einem Installationsprogramm (MSI, WiX’ ServiceInstall o. Ä.), das Registrierung, Aktualisierung und Entfernung als Einheit übernimmt. Eine manuelle Registrierungsanleitung erzeugt zwangsläufig irgendwann einen Rechner, auf dem ein Schritt übersprungen wurde.
5.2 Wiederherstellungsoptionen ── „Bei Absturz neu starten“ dem SCM überlassen
Einer der großen Vorteile, etwas zu einem Dienst zu machen, sind die standardmäßigen Wiederherstellungsoptionen des SCM. Sie können deklarativ konfigurieren, was passiert, wenn der Prozess abnormal beendet wird.2
sc.exe failure "KsMonitor" reset= 86400 actions= restart/60000/restart/60000/restart/300000
Dieses Beispiel bedeutet: „Beim 1. und 2. Fehler nach 60 Sekunden neu starten, ab dem 3. Fehler nach 5 Minuten neu starten, der Fehlerzähler wird nach 24 Stunden zurückgesetzt.“ Dieselbe Einstellung lässt sich auch über die GUI vornehmen (services.msc → Eigenschaften → Registerkarte „Wiederherstellung“). Greifen Sie zu dieser Standardfunktion, bevor Sie eine eigene „Überwachungsaufgabe“ oder ein Überwachungsskript bauen.
Zwei Punkte sind zu beachten. Erstens greift dies, wie im vorigen Kapitel beschrieben, nur, wenn der Prozess mit einem Wert ungleich 0 beendet wird. Ein normales Beenden durch StopHost fällt nicht unter die Wiederherstellung. Zweitens führt ein Zustand, in dem der Dienst direkt nach dem Start garantiert abstürzt (Fehlkonfiguration, unerreichbare Datenbank), zu einer Neustartschleife. Genau deshalb sollte der Abstand ab dem 3. Fehler länger sein, und genau deshalb sollten Sie vorher sicherstellen, dass „warum er abgestürzt ist“ im Ereignisprotokoll festgehalten wird (nächster Abschnitt).
5.3 Ereignisprotokoll und Dateiprotokoll kombiniert einsetzen
Host.CreateApplicationBuilder fügt unter Windows automatisch den EventLog-Protokollierungsanbieter hinzu. Und standardmäßig gelangen nur Meldungen ab Warning ins Ereignisprotokoll.2 Man verwechselt das leicht mit „nachdem ich es zu einem Dienst gemacht habe, sind alle Information-Protokolle verschwunden“ – sie sind nicht verschwunden, sondern werden vom Standardfilter des Ereignisprotokolls abgefangen. Wollen Sie auch Betriebsereignisse wie Start und Stopp protokollieren, legen Sie die Stufe des EventLog-Anbieters explizit in appsettings.json fest.
{
"Logging": {
"EventLog": {
"SourceName": "KsMonitor",
"LogLevel": {
"Default": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
}
Ein Hinweis: Eine Ereignisquelle kann erst beschrieben werden, wenn sie vorher registriert wurde, und die Registrierung erfordert Administratorrechte. Auch das Weglassen von SourceName hilft hier nicht – standardmäßig verwendet AddWindowsService den Anwendungsnamen als Quellname2, sodass Sie ohnehin bei einer „nicht registrierten Quelle“ starten. Ein Konto mit minimalen Rechten wie LocalService kann zur Laufzeit keine Quelle erstellen, und schlägt die Erstellung fehl, beginnt der Betrieb, ohne dass überhaupt etwas ins Ereignisprotokoll geschrieben wird. Erledigen Sie die Registrierung auf der Seite des Installationsprogramms (oder in einem Einrichtungsschritt mit Administratorrechten). Das ist derselbe Punkt, den Kapitel 6 des Artikels zur Aufgabenplanung unter „CreateEventSource in den Einrichtungsschritt auslagern“ beschreibt.
Als Faustregel für die Aufgabenteilung gilt: Ins Ereignisprotokoll gehört nur, was ein Betreiber sehen muss (Start, Stopp, Fehler, Wiederherstellung), und detaillierte Verarbeitungsverfolgung gehört in Ihr eigenes Dateiprotokoll. Die Mindestanforderungen für ein eigenes Dateiprotokoll (Rotation, Verhalten bei Schreibfehlern) sind in „Die Mindestanforderungen an einen selbst geschriebenen Logger“ zusammengefasst, und ein Entwurf, der beim Absturz des gesamten Prozesses Beweise hinterlässt, in „Entwurf, um bei einem Absturz Protokolle und Dumps zu hinterlassen“. Bei einem Aufbau, bei dem die Wiederherstellungsoptionen einen automatischen Neustart auslösen, ist das, was im Moment des Absturzes tatsächlich aufgezeichnet wurde, der einzige Anhaltspunkt, den Sie haben.
5.4 Prüfen, ob die Registrierung erfolgreich war ── Wo in Befehl und GUI nachsehen
Nach der Registrierung sehen Sie der Reihe nach an drei Stellen nach, um zu bestätigen, „ob der Dienst mit der beabsichtigten Konfiguration und dem beabsichtigten Konto läuft“.
Konfiguration prüfen (Befehl). sc.exe qc "KsMonitor" zeigt die registrierten Angaben. Zu prüfen sind drei Zeilen: BINARY_PATH_NAME (zeigt es auf die veröffentlichte exe?), START_TYPE (Automatisch, verzögert automatisch oder manuell?) und SERVICE_START_NAME (ist das Ausführungskonto das in Kapitel 6 festgelegte?). Den Ausführungsstatus sehen Sie über STATE bei sc.exe query "KsMonitor", unter PowerShell mit Get-Service KsMonitor. Den aktuellen Wert der Wiederherstellungsoptionen können Sie mit sc.exe qfailure "KsMonitor" auslesen.
Konfiguration prüfen (GUI). Öffnen Sie mit der Windows-Taste + R → „services.msc“ die Dienste-Verwaltung (dasselbe Fenster erreichen Sie auch über „Computerverwaltung“ → „Dienste und Anwendungen“ → „Dienste“). Klicken Sie den betreffenden Dienst mit der rechten Maustaste an → „Eigenschaften“: Die Registerkarte „Allgemein“ zeigt den Starttyp und die in 5.1 gesetzte Beschreibung, die Registerkarte „Anmelden“ das Ausführungskonto, und die Registerkarte „Wiederherstellung“ das Verhalten beim ersten, zweiten und jedem weiteren Fehler.
Ereignisprotokoll prüfen. Öffnen Sie mit der Windows-Taste + R → „eventvwr.msc“ die Ereignisanzeige, dort unter „Windows-Protokolle“ → „System“ die Ereignisse der Quelle „Service Control Manager“. Hier finden sich die Ereignisse 7000/70117, wenn der Start nicht rechtzeitig als abgeschlossen gemeldet wurde, sowie 7031 (mit Wiederherstellungsaktion) und 7034 (ohne Wiederherstellungsaktion), wenn der Dienst abnormal beendet wurde.8 Die eigenen Protokolle der Anwendung, deren Stufe Sie in 5.3 festgelegt haben, finden Sie dagegen unter „Windows-Protokolle“ → „Anwendung“, da der EventLog-Anbieter standardmäßig in „Application“ schreibt9; filtern Sie dort nach dem Quellnamen (SourceName, standardmäßig der Anwendungsname2). Merken Sie sich: Der Lebenszustand des Dienstes steht unter „System“, der Inhalt der Anwendung unter „Anwendung“ – dann müssen Sie im Störungsfall nicht lange suchen.
6. Das Ausführungskonto ── Wählen Sie LocalSystem nicht aus Trägheit
Ein Dienst läuft im Sicherheitskontext des angegebenen Kontos; beim Start meldet sich der SCM mit diesem Konto an und weist dem Prozess dessen Token zu.10 Das ist die Dienst-Variante der Frage „als wer wird das ausgeführt“, die wir in Kapitel 3 des Artikels zur Aufgabenplanung behandelt haben. Hier die Optionen im Überblick.
| Konto | Rechte | Netzwerkidentität | Passwortverwaltung | Einsatzbereich |
|---|---|---|---|---|
| LocalSystem | Extrem stark (entspricht dem Betriebssystem) | Computerkonto | Nicht erforderlich | Grundsätzlich vermeiden. Vorsicht: Standard von sc.exe create |
| LocalService | Minimal | Anonym (kein Zugriff auf Freigaben) | Nicht erforderlich | Erste Wahl für rein lokale Verarbeitung |
| NetworkService | Minimal | Computerkonto | Nicht erforderlich | Leichte Verarbeitung mit Zugriff auf Domänenressourcen |
| Virtuelles Konto (NT SERVICE\Dienstname) | Nur das Gewährte | Computerkonto | Nicht erforderlich | ACLs können pro Dienst vergeben werden. Minimale Rechte für lokale Ressourcen |
| gMSA | Nur das Gewährte | Das gMSA selbst | Wird vom DC automatisch verwaltet | Erste Wahl für einen Dienst, der in einer Domäne auf Freigaben/Datenbanken zugreift |
Drei Punkte sind entscheidend.
- Wählen Sie LocalSystem nicht, „weil es funktioniert“. Eine Schwachstelle im Dienst führt unmittelbar zur Übernahme der gesamten Maschine. Ob administratoräquivalente Rechte wirklich nötig sind, lässt sich mit der Einordnung aus „Wann werden unter Windows tatsächlich Administratorrechte benötigt?“ direkt beurteilen. In den meisten Fällen genügt etwas wie „Schreibzugriff auf einen bestimmten Ordner“, und dafür reicht es, einem virtuellen Konto (
NT SERVICE\KsMonitor) eine ACL auf den Ordner zu gewähren. Ein virtuelles Konto braucht weder Erstellung noch Passwortverwaltung.5 - Die Falle mit Netzwerkfreigaben. LocalService tritt im Netzwerk anonym auf, sodass der Zugriff auf
\\server\freigabefehlschlägt. NetworkService, virtuelle Konten und LocalSystem treten alle als Computerkonto (DOMÄNE\Rechnername$) im Netzwerk auf5, sodass die Freigabe sowohl auf Freigabe- als auch auf NTFS-Ebene dem Computerkonto Berechtigungen erteilen muss. „Dem Benutzer habe ich Berechtigungen erteilt, aber nur der Dienst kann nicht lesen“ hat fast immer diese Ursache. Legen Sie zuerst fest, als wer der Dienst im Netzwerk auftritt, und konfigurieren Sie erst danach die Freigabeseite. - Läuft der Dienst unter einem gewöhnlichen Benutzerkonto, planen Sie die Passwortverwaltung mit ein. Ein dediziertes Konto benötigt das Recht „Anmelden als Dienst“, und läuft das Passwort ab, schlägt die Anmeldung fehl und der Dienst kann nicht starten.10 Das ist die Dienst-Variante des Problems „die Aufgabe stirbt still nach einer Passwortänderung“. In einer Domänenumgebung ist es die richtige Lösung, dieses Problem mit einem gMSA, dessen Passwort die Domäne automatisch verwaltet, komplett zu beseitigen (dieselbe Schlussfolgerung wie in Abschnitt 3.2 des Artikels zur Aufgabenplanung).
7. Sicheres Beenden ── ShutdownTimeout und laufende Verarbeitung
Sehen wir uns den Ablauf des Beendens an. Löst services.msc, sc.exe stop oder ein Herunterfahren des Betriebssystems beim SCM eine Stoppanforderung aus, übersetzt die .NET-Dienstlebensdauer dies in ein Stoppen des Hosts (StopApplication), stoppingToken wird abgebrochen, ExecuteAsync wird verlassen, und StopAsync jedes Dienstes wird aufgerufen. Wie lange der Host auf diesen gesamten Beendigungsablauf wartet, legt HostOptions.ShutdownTimeout fest, standardmäßig 30 Sekunden.611 Reicht die Zeit nicht, wird das Beenden erzwungen, ohne auf den Abschluss der Nachbereitung zu warten.
Reichen 30 Sekunden nicht aus, um ein laufendes Element vollständig zu schreiben, verlängern Sie das Timeout ausgehend von der tatsächlich benötigten Zeit.
builder.Services.Configure<HostOptions>(options =>
{
// Ausgehend von der maximal benötigten Zeit, um laufende Verarbeitung vollständig abzuschließen
options.ShutdownTimeout = TimeSpan.FromSeconds(90);
});
Der Entwurf der Beendigungslogik selbst besteht anschließend aus drei Punkten.
- Legen Sie im Voraus als Reihenfolge fest, was nach der Stoppanforderung geschehen soll. Keine neuen Aufträge mehr annehmen → laufende Verarbeitung abschließen (oder an einer sicheren Stelle unterbrechen und in einer fortsetzbaren Form aufzeichnen) → Verbindungen und temporäre Dateien aufräumen. Legen Sie diese Reihenfolge nicht vorher fest, geraten Sie in eines von zwei Extremen: alles fallen zu lassen, sobald das Token gesehen wird, oder zu versuchen, alles vollständig abzuschließen, und dabei die Zeit zu überschreiten.
- Bauen Sie keinen „Dienst, der nicht auf Stoppanforderungen reagiert“. Schon eine einzige lange Schleife oder ein einziges langes Warten, das das Token ignoriert, macht daraus einen Dienst, der im SCM dauerhaft als „wird beendet“ hängen bleibt. Das ist im Betrieb der meistgehasste Fehler: Er blockiert den Neustart nach Windows-Updates und erzwingt bei jeder geplanten Serverabschaltung ein manuelles Kill. Prüfen Sie, wie in Kapitel 4 beschrieben, an jeder Verarbeitungsgrenze das Token, und übergeben Sie langen I/O-Operationen ein
CancellationToken. Da Strg+C bei der Konsolenausführung genau diesen Beendigungsablauf durchläuft, empfehlen wir, „wie viele Sekunden nach Strg+C ist der Vorgang beendet“ als Testpunkt vor der Veröffentlichung aufzunehmen. - Stellen Sie einen Entwurf, der auch ein Kill übersteht, vor die Sorgfalt der Beendigungslogik. Ein Stromausfall oder ein erzwungenes Beenden passiert, egal wie sorgfältig Sie die Beendigungslogik bauen. Transaktionen, atomare Dateischreibvorgänge, wiederholbare Verarbeitungseinheiten – ein Aufbau, bei dem „selbst wenn es mittendrin stirbt, beim nächsten Start wieder Konsistenz hergestellt wird“, ist die eigentliche Substanz; eine saubere Beendigungslogik ist letztlich nur eine Frage des guten Anstands.
8. Zusammenfassung
Die Entscheidung ist einfach. Periodische Verarbeitung gehört in die Aufgabenplanung, ständiges Warten, Reaktionen im Sekundenbereich und automatische Wiederherstellung brauchen einen Windows-Dienst, und ist die UI der eigentliche Zweck, ist es eine residente Anwendung. Haben Sie sich für einen Dienst entschieden, ist der aktuelle Standardweg, mit dem .NET-Worker-Service und AddWindowsService „als Konsole zu entwickeln und als Dienst auszuliefern“.
Bei der Registrierung sind vier Punkte als Entwurfsentscheidung zu treffen – Starttyp (bei Fachanwendungsdiensten verzögerter Start), Ausführungskonto (LocalSystem vermeiden, virtuelles Konto oder gMSA), Wiederherstellungsoptionen (sc.exe failure zusammen mit Environment.Exit(1)) und Beenden (ShutdownTimeout und die Nachbereitung laufender Verarbeitung). Beherrschen Sie diese vier Punkte und eine ExecuteAsync-Implementierung, die stoppingToken respektiert, ist ein Windows-Dienst nichts mehr, „das besonders schwer zu bauen wäre“. Umgekehrt kommt ein Dienst, der ohne diese vier Entscheidungen zu laufen beginnt, Jahre später mit einem dreifachen Fluch zurück: Er lässt sich nicht stoppen, sein Absturz bleibt unbemerkt, und seine Rechte sind zu stark, um ihn gefahrlos anzufassen. Auch die Sanierung eines bestehenden Dienstes beginnt am schnellsten mit einer Bestandsaufnahme genau dieser vier Punkte.
Verwandte Artikel
- Aufgaben der Aufgabenplanung laufen nicht oder enden mit 0x1 ── Ursachenanalyse und Entwurf für einen zuverlässigen Betrieb
- Was ist der .NET Generic Host?
- Entscheidungstabelle für Interprozesskommunikation
- Entwurf, um bei einem Absturz Protokolle und Dumps zu hinterlassen
Verwandte Beratungsleistungen
Die KomuraSoft LLC übernimmt Design-Reviews für residente Verarbeitung (einschließlich der Entscheidung über eine Migration weg von der Aufgabenplanung) sowie die Untersuchung und Sanierung von Windows-Diensten, die „nicht auf Stoppanforderungen reagieren“ oder „unbemerkt abstürzen“.
Referenzlinks
-
Microsoft Learn, Interactive Services. Dazu, dass Dienste seit Windows Vista nicht direkt mit dem Benutzer interagieren können, dass Dienste in Sitzung 0 ausgeführt werden, sowie dazu, dass bei Bedarf an einer UI ein Entwurf mit einer separaten GUI-Anwendung empfohlen wird, die über IPC mit dem Dienst kommuniziert. ↩ ↩2 ↩3
-
Microsoft Learn, Create Windows Service using BackgroundService. Das offizielle Tutorial, um einen Worker Service in einen Windows-Dienst umzuwandeln. Zu
AddWindowsService, dazu, dass der Standard des EventLog-Anbieters Warning ist, zur Registrierung/Wiederherstellung/Entfernung übersc.exe, sowie dazu, dass die Wiederherstellungsoptionen einen Exit-Code ungleich 0 voraussetzen. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 -
Microsoft Learn, .NET 6 breaking change: Exception handling in hosting. Dazu, dass ab .NET 6 eine unbehandelte Ausnahme in
BackgroundService.ExecuteAsyncprotokolliert wird und standardmäßig den Host stoppt (BackgroundServiceExceptionBehavior.StopHost). ↩ ↩2 -
Microsoft Learn, sc.exe create. Zur Spezifikation von
start=(auto/demand/delayed-auto usw.) undobj=(Standard LocalSystem) sowie dazu, dass nach dem Gleichheitszeichen jeder Option ein Leerzeichen zwingend erforderlich ist (das Weglassen führt zum Fehlschlagen des Befehls). ↩ ↩2 ↩3 ↩4 -
Microsoft Learn, Service Accounts in Windows Server. Dazu, dass virtuelle Konten (
NT SERVICE\Dienstname) ohne jede Passwortverwaltung mit den Anmeldeinformationen des Computerkontos auf das Netzwerk zugreifen, sowie dazu, dass das Passwort eines gMSA automatisch auf Seiten der Domäne verwaltet wird. ↩ ↩2 ↩3 -
Microsoft Learn, .NET Generic Host. Zum Ablauf der Host-Beendigung und zum konfigurierbaren Shutdown-Timeout (Standard 30 Sekunden), auf das beim Stoppen gewartet wird. ↩ ↩2
-
Microsoft Learn, A service does not start, and events 7000 and 7011 are logged. Dazu, dass der SCM nur so lange wie in
ServicesPipeTimeoutangegeben auf den Start eines Dienstes wartet und bei Überschreitung die Ereignisse 7000/7011 protokolliert, sowie zum Vorgehen zum Anheben des Standard-Timeouts. ↩ ↩2 -
Microsoft Learn (Archiv), Event ID 7031 — Service Stop Operations und Event ID 7034 — Service Stop Operations. Dazu, dass der SCM ein unerwartetes Beenden eines Dienstes sowie das Fehlschlagen eines Neustarts nach der Wiederherstellungsaktion protokolliert; dass Ereignis 7031 (Symbolname
EVENT_SERVICE_CRASH) die Meldung „Der Dienst wurde unerwartet beendet. Dies ist das N-te Mal. Die folgende Korrekturmaßnahme wird in M Millisekunden ausgeführt“ trägt, während 7034 (SymbolnameEVENT_SERVICE_CRASH_NO_ACTION) keine Beschreibung einer Korrekturmaßnahme enthält; dass beide als Quelle Service Control Manager haben; sowie dazu, dass sich die Korrekturmaßnahme über die Registerkarte „Wiederherstellung“ der Eigenschaften im Snap-In „Dienste“ ändern lässt. ↩ -
Microsoft Learn, EventLogSettings.LogName Property. Dazu, dass der Name des Ziel-Ereignisprotokolls des EventLog-Protokollierungsanbieters standardmäßig „Application“ ist, wenn nichts angegeben wird. ↩
-
Microsoft Learn, Service User Accounts. Dazu, dass ein Dienst im Sicherheitskontext des angegebenen Kontos ausgeführt wird, dass sich der SCM beim Start anmeldet und der Dienst nicht starten kann, wenn das Passwort abgelaufen ist, sowie zu den besonderen Konten LocalService/NetworkService/LocalSystem. ↩ ↩2
-
Microsoft Learn, HostOptions.ShutdownTimeout Property. Zur Definition der Eigenschaft, die das Standard-Timeout für
StopAsyncsteuert. ↩
Verwandte Artikel
Aktuelle Artikel mit denselben Schlagwörtern führen zu verwandten Themen weiter.
Datum, Uhrzeit und Zeitzonen in Business-Apps — Von den Fallstricken von DateTime über das UTC-Speicherprinzip bis zum Testdesign
Zeitstempel verschieben sich nach einer Servermigration um neun Stunden; nur beim Auslandsbüro springt das Datum auf den Vortag zurück – ...
Windows-Prozesskommunikation richtig wählen ── Eine Entscheidungstabelle für Named Pipes / TCP / gRPC / Shared Memory / COM
Wie wählt man den richtigen Weg, damit Windows-Anwendungen miteinander sprechen? Dieser Artikel ordnet Named Pipes, lokales TCP, gRPC, Sh...
SQLite aus C# in Business-Apps nutzen — WAL-Modus, exklusive Sperren, Schutz vor Datenbankbeschädigung und wann sich EF Core lohnt
Ein praxisnaher Überblick zur Einbindung von SQLite in eine Windows-Businessanwendung mit Microsoft.Data.Sqlite: Verbindungszeichenfolgen...
Wie Windows-Apps lokale Daten speichern sollten — Entscheidungstabelle für SQLite / JSON / Registrierung / Access
Wo und womit sollte eine Windows-Desktopanwendung ihre Daten speichern? Dieser Artikel ordnet die Wahl zwischen AppData und ProgramData s...
Eine Legacy-Business-Anwendung ohne Tests sicher verändern ── Charakterisierungstests und Refactoring in der Praxis
Um eine Business-Anwendung ohne Tests sicher zu verändern, erklärt dieser Artikel anhand von C#-Beispielen das Vorgehen bei einem Charakt...
Verwandte Themen
Diese Seiten ordnen den Artikel in einen größeren Leistungs- und Entscheidungskontext ein.
Technische Windows-Themen
Portal zu Windows-Entwicklung, Fehleranalyse und der Nutzung bestehender Assets.
Generic Host und Anwendungsarchitektur
Generic Host, BackgroundService, DI, Konfiguration, Protokollierung und Anwendungslebenszyklus.
Leistungen zu diesem Thema
Dieser Artikel ist direkt mit den folgenden Leistungen verbunden.
Windows-App-Entwicklung
Geschäftsanwendungen, Geräteintegration und Kommunikationstools von den Anforderungen bis zur Umsetzung.
Häufige Fragen
Fragen, die in Beratungen zu diesem Artikelthema häufig gestellt werden.
- Wie unterscheidet man zwischen Aufgabenplanung und Windows-Dienst?
- Bei periodischen Aufgaben im Abstand von mehreren Minuten oder mehr, die zwischen den Durchläufen keinen Zustand halten, reicht die Aufgabenplanung. Sobald ständiges Warten auf TCP- oder Pipe-Verbindungen, eine Ordnerüberwachung mit FileSystemWatcher, eine Reaktionszeit im Sekundenbereich oder eine automatische Wiederherstellung nach einem Absturz zu den Anforderungen gehören, brauchen Sie einen Windows-Dienst. Wenn Sie anfangen, mit einer „Aufgabe alle 5 Minuten“ in kleine Häppchen zu pollen, ist das ein Zeichen dafür, dass Sie einen residenten Prozess schlecht nachbauen. Ist die Benutzeroberfläche der eigentliche Zweck (z. B. Bedienung über das Infobereichssymbol), gibt es eine dritte Option: eine residente Anwendung, die nur läuft, solange jemand angemeldet ist.
- Kann ein Windows-Dienst eine Benutzeroberfläche anzeigen?
- Nein. Seit Windows Vista laufen Dienste in einer isolierten Sitzung namens Sitzung 0 und können nicht direkt mit dem Benutzer interagieren. Ruft man innerhalb eines Dienstes MessageBox.Show auf, erscheint auf dem Bildschirm des angemeldeten Benutzers nichts – stattdessen bleibt die Verarbeitung hängen, weil sie endlos auf eine OK-Schaltfläche wartet, die niemand klicken kann. Wird eine Benutzeroberfläche benötigt, ist die richtige Lösung, Dienst und UI-Anwendung in getrennte Prozesse aufzuteilen, die über Interprozesskommunikation wie benannte Pipes miteinander kommunizieren. Beim Portieren von altem Code in einen Dienst sollten Sie unbedingt prüfen, ob nicht noch eine als Fehleranzeige gedachte Meldungsbox übrig geblieben ist.
- Wie erstellt man mit .NET einen Windows-Dienst?
- Der offizielle Weg beginnt mit dem Worker-Service-Template (dotnet new worker); anschließend fügen Sie das Paket Microsoft.Extensions.Hosting.WindowsServices hinzu und rufen AddWindowsService auf. Dieselbe exe läuft unter Visual Studios F5 oder dotnet run als ganz normale Konsolenanwendung, was Entwicklung und Debugging erheblich erleichtert. Die Registrierung erfolgt mit sc.exe create – dabei ist zu beachten, dass nach dem Gleichheitszeichen von binpath= oder start= zwingend ein Leerzeichen stehen muss, und dass beim Weglassen von obj= standardmäßig LocalSystem (die stärksten Rechte) verwendet wird. Da das aktuelle Verzeichnis beim Start als Dienst C:\Windows\System32 ist, sollten Konfigurationsdateien relativ zur ausführbaren Datei aufgelöst werden.
- Was passiert mit dem Dienst, wenn im BackgroundService eine Ausnahme auftritt?
- Ab .NET 6 wird eine unbehandelte Ausnahme, die aus ExecuteAsync entweicht, standardmäßig (BackgroundServiceExceptionBehavior.StopHost) protokolliert und stoppt anschließend den Host. Da dies jedoch als „normales Beenden“ gilt, lösen die Wiederherstellungsoptionen des SCM (automatischer Neustart) nicht aus. Ein Fehler, der über die Wiederherstellungsoptionen zu einem Neustart führen soll, muss den Prozess explizit mit einem Exit-Code ungleich 0 abnormal beenden, etwa über Environment.Exit(1). Fortsetzbare Fehler wie einen Netzwerkabbruch fangen Sie innerhalb der Schleife ab, protokollieren und wiederholen sie – getrennt von nicht behebbaren Fehlern, die diese Behandlung nicht erhalten.
Autorenprofil
Profilseite des Artikelautors.
Go Komura
Geschäftsführer von KomuraSoft LLC
Spezialisiert auf Windows-Softwareentwicklung, technische Beratung und Fehleranalyse, insbesondere bei bestehenden Systemen und schwer reproduzierbaren Störungen.