Gdy aplikacja ma działać po restarcie komputera, bez zalogowanego użytkownika i bez otwierania okna, zwykły program desktopowy szybko przestaje wystarczać. W takich sytuacjach przydają się windows services, czyli usługi Windows, które mogą pracować w tle, reagować na zdarzenia systemowe i automatycznie się uruchamiać. Wyjaśniam, jak działają, kiedy warto je stosować, jak zarządzać nimi w systemie oraz jak przygotować usługę w .NET.
Usługi Windows zapewniają aplikacjom stabilną pracę w tle
- Usługa systemowa działa bez interfejsu użytkownika i może wystartować przed zalogowaniem.
- Service Control Manager odpowiada za uruchamianie, zatrzymywanie, konfigurację i monitorowanie usług.
- Automatyczny start pasuje do procesów, które muszą być stale dostępne, ale nie każda aplikacja powinna działać jako usługa.
- .NET Worker Service to obecnie wygodny punkt wyjścia dla nowych usług tworzonych w C#.
- Najczęstsze problemy wynikają z uprawnień, złych ścieżek, braku logowania i próby używania interfejsu graficznego.
Czym jest usługa Windows i jak działa
Usługa Windows to aplikacja działająca w tle, której cyklem życia zarządza systemowy Service Control Manager. Ten komponent przechowuje informacje o zainstalowanych usługach, uruchamia je zgodnie z konfiguracją, przekazuje polecenia startu i zatrzymania oraz śledzi ich stan.
Najważniejsza różnica względem zwykłego programu polega na tym, że usługa nie potrzebuje aktywnej sesji użytkownika. Może rozpocząć pracę podczas uruchamiania systemu, działać przez wiele dni i zakończyć się dopiero przy zatrzymaniu komputera albo wydaniu odpowiedniego polecenia. Nie pokazuje przy tym własnego okna i nie powinna wymagać kliknięć użytkownika.
W konsoli usług spotkasz kilka podstawowych stanów. Usługa może być uruchomiona, zatrzymana, wstrzymana albo znajdować się w stanie przejściowym, na przykład podczas startu lub zatrzymywania. Dostępne są również różne tryby uruchamiania.
| Tryb | Zastosowanie | Praktyczna uwaga |
|---|---|---|
| Automatyczny | Procesy wymagające stałej dostępności | Usługa startuje przy uruchamianiu systemu |
| Automatyczny z opóźnieniem | Programy, które nie muszą startować jako pierwsze | Zmniejsza obciążenie podczas startu Windows |
| Ręczny | Usługi uruchamiane na żądanie | Start może nastąpić przez administratora lub mechanizm wyzwalający |
| Wyłączony | Usługi, których nie wolno uruchamiać | System zablokuje próbę startu do czasu zmiany konfiguracji |
Usługi mogą też reagować na wyzwalacze, takie jak pojawienie się urządzenia, zmiana sieci albo dołączenie komputera do domeny. To rozsądniejszy model niż stałe działanie, gdy aplikacja ma reagować tylko na konkretne zdarzenia. W praktyce ogranicza zużycie zasobów i liczbę procesów pracujących bez potrzeby.
Kiedy usługa ma sens, a kiedy lepszy jest inny mechanizm
Nie każdą aplikację działającą w tle warto instalować jako usługę. Ja zaczynam od pytania, czy proces ma działać niezależnie od użytkownika i czy jego praca powinna być kontrolowana przez system operacyjny. Jeżeli odpowiedź brzmi „tak”, usługa zwykle jest dobrym wyborem.
| Mechanizm | Najlepsze zastosowanie | Kiedy go nie wybierać |
|---|---|---|
| Usługa Windows | Stała praca serwera, agenta, synchronizatora lub kolejki | Gdy program wymaga interfejsu graficznego |
| Zadanie w Harmonogramie zadań | Cykliczne raporty, kopie zapasowe i skrypty uruchamiane o określonej porze | Gdy proces musi stale nasłuchiwać zdarzeń |
| Aplikacja uruchamiana przy logowaniu | Narzędzia użytkownika, synchronizacja profilu, aplikacje z ikoną w zasobniku | Gdy zadanie ma działać przed logowaniem |
| Proces konsolowy | Diagnostyka, jednorazowe migracje i lokalne narzędzia administracyjne | Gdy potrzebny jest automatyczny restart po awarii |
Dobrym przykładem jest agent monitorujący katalog. Jeśli ma obserwować pliki przez całą dobę i wysyłać je do Azure, usługa zapewnia przewidywalny start oraz możliwość działania bez sesji użytkownika. Jeśli jednak wystarczy uruchomić skrypt raz dziennie o 2:00, Harmonogram zadań będzie prostszy i łatwiejszy w utrzymaniu.
Trzeba też pamiętać o kosztach stałej pracy. Usługa zużywa pamięć, otwarte uchwyty, połączenia sieciowe i czas procesora. Program, który wykonuje zadanie raz na godzinę, nie powinien bezczynnie działać przez cały dzień tylko dlatego, że można go uruchomić jako usługę.
Jak zarządzać usługami w codziennej pracy
Najprostsze narzędzie to konsola usług dostępna po uruchomieniu services.msc. Można w niej sprawdzić stan, typ uruchamiania, konto, zależności oraz ustawienia reakcji na awarię. Dla pojedynczej maszyny jest wygodna, ale przy większej liczbie serwerów szybciej pracuje się z PowerShellem.
Get-Service
Get-Service -Name Spooler
Start-Service -Name Spooler
Stop-Service -Name Spooler
Restart-Service -Name SpoolerDo odczytu konfiguracji przydaje się również sc.exe. Narzędzie pozwala między innymi sprawdzić ścieżkę pliku wykonywalnego, konto usługi, zależności i tryb startu.
sc.exe query Spooler
sc.exe qc Spooler
sc.exe config Spooler start= delayed-autoW poleceniach sc.exe spacja po znaku równości jest częścią składni. To drobiazg, który regularnie powoduje błędy podczas ręcznej konfiguracji. Zmiany wymagają też uprawnień administratora, a niektóre usługi nie pozwalają na zatrzymanie, jeśli korzystają z nich inne komponenty.
Przy problemach nie zaczynam od przypadkowego restartowania usług. Najpierw sprawdzam Podgląd zdarzeń, szczególnie dzienniki System i Application, a potem konfigurację usługi, konto oraz zależności. Komunikat „usługa nie została uruchomiona” jest zbyt ogólny, aby na jego podstawie zgadywać przyczynę.
Jak utworzyć usługę w .NET
W nowych projektach .NET najwygodniej użyć szablonu Worker Service. Aplikacja opiera się na BackgroundService, czyli klasie przeznaczonej do długotrwałej pracy w tle. Ten model dobrze współpracuje z wstrzykiwaniem zależności, konfiguracją, logowaniem i tokenem anulowania.
Rejestracja usługi w pliku programu może wyglądać tak:
using Microsoft.Extensions.Hosting;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddWindowsService(options =>
{
options.ServiceName = "Kursdotnet Worker";
});
builder.Services.AddHostedService();
IHost host = builder.Build();
host.Run(); Metoda AddWindowsService konfiguruje hosta tak, aby aplikacja mogła działać pod kontrolą menedżera usług. Sama klasa robocza powinna reagować na anulowanie pracy, zwalniać zasoby i nie blokować bez końca metody startowej.
public sealed class Worker : BackgroundService
{
private readonly ILogger _logger;
public Worker(ILogger logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(
CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Przetwarzanie kolejnej partii danych");
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
}
} Po publikacji aplikacji można zarejestrować ją poleceniem:
sc.exe create KursdotnetWorker ^
binPath= "C:\Apps\KursdotnetWorker\KursdotnetWorker.exe" ^
start= auto ^
DisplayName= "Kursdotnet Worker"Wdrożenie warto przygotować jako pojedynczy plik wykonywalny albo przynajmniej jako kompletny katalog publikacji. Najczęstszy błąd polega na wskazaniu pliku z katalogu projektu, który działa lokalnie, lecz po przeniesieniu na serwer traci zależności lub ma nieprawidłową ścieżkę roboczą.
Podczas tworzenia aplikacji uruchamiam ją najpierw jako zwykły proces konsolowy. Debugowanie usługi przez instalowanie jej po każdej zmianie jest wolne i niewygodne. Dopiero gdy logika przetwarzania działa poprawnie, testuję start, zatrzymanie, restart i zachowanie po awarii pod kontrolą Windows.
Bezpieczeństwo, logowanie i diagnozowanie awarii
Usługa działa na określonym koncie. Do wyboru są między innymi LocalSystem, LocalService, NetworkService oraz dedykowane konto użytkownika lub konto zarządzane. LocalSystem ma bardzo szerokie uprawnienia, dlatego używanie go bez wyraźnego powodu zwiększa skutki ewentualnego błędu lub włamania.
Najbezpieczniejsza zasada brzmi: przyznaj usłudze tylko dostęp do katalogów, udziałów, certyfikatów i baz danych, których naprawdę potrzebuje. Konto może mieć prawo odczytu bez prawa zapisu, a dostęp do sekretów powinien być ograniczony do minimum. Szczególną ostrożność zachowuję przy połączeniach do udziałów sieciowych, ponieważ konto lokalne może nie mieć tam żadnych praw.
Usługa nie powinna zakładać, że bieżący katalog jest katalogiem aplikacji. Windows może uruchomić proces z innej lokalizacji, dlatego ścieżki do plików konfiguracyjnych i logów trzeba budować świadomie. W przeciwnym razie aplikacja działa z terminala, ale po instalacji jako usługa nagle „nie znajduje” pliku.
Logowanie do pliku jest przydatne, ale nie powinno być jedynym źródłem informacji. Dla usług .NET sensowne jest połączenie Event Log, strukturalnych logów aplikacyjnych i monitoringu procesu. Logi powinny zawierać czas, identyfikator operacji, istotne parametry oraz pełny wyjątek, ale bez haseł i tokenów.
W konfiguracji warto ustawić reakcję na awarię, na przykład ponowny start po 1 minucie oraz zapis zdarzenia w dzienniku. Automatyczny restart pomaga przy przejściowych problemach, lecz nie naprawia błędnej konfiguracji. Jeśli aplikacja kończy się natychmiast po każdym uruchomieniu, system może jedynie wielokrotnie powtarzać ten sam błąd.
Usługa, która nie zaskakuje po wdrożeniu
Dobrze zaprojektowana usługa ma jasno określony zakres odpowiedzialności, przewidywalny cykl życia i możliwość zatrzymania bez utraty danych. Przed wdrożeniem sprawdzam co najmniej start po restarcie, działanie bez zalogowanego użytkownika, brak dostępu do pulpitu, utratę sieci, brak uprawnień oraz poprawne zamknięcie procesu.
Najlepszy efekt daje mały, wyspecjalizowany proces, który robi jedną rzecz i robi ją dobrze. Usługa nie powinna być workiem na przypadkowe zadania tylko dlatego, że system potrafi ją uruchomić automatycznie. Gdy logowanie, uprawnienia i obsługa anulowania są przygotowane od początku, późniejsze utrzymanie aplikacji .NET na Windows staje się znacznie spokojniejsze.
