Poznámka
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Vytvoření služby systému Windows pomocí
Vývojáři rozhraní .NET Framework jsou pravděpodobně obeznámeni s aplikacemi služby pro Windows. Před .NET Core a .NET 5+ mohou vývojáři, kteří se spoléhali na rozhraní .NET Framework, vytvářet služby Windows k provádění úloh na pozadí nebo spouštět dlouhotrvající procesy. Tato funkce je stále dostupná a můžete vytvořit služby pracovního procesu, které běží jako služba systému Windows.
V tomto kurzu se naučíte:
- Publikujte pracovní aplikaci .NET jako jeden spustitelný soubor.
- Vytvořte službu systému Windows.
- Vytvořte
BackgroundService
aplikaci jako službu pro Windows. - Spusťte a zastavte službu systému Windows.
- Zobrazení protokolů událostí
- Odstraňte službu systému Windows.
Rada
Zdrojový kód "Pracovní procesy v .NET" je k dispozici v prohlížeči ukázek ke stažení. Další informace najdete v tématu Prohlédněte si ukázky kódu: pracovníky v .NET.
Důležitý
Instalace sady .NET SDK také nainstaluje Microsoft.NET.Sdk.Worker
a pracovní šablonu. Jinými slovy, po instalaci sady .NET SDK můžete vytvořit nový pracovní proces pomocí příkazu dotnet new worker. Pokud používáte Visual Studio, šablona se skryje, dokud se nenainstaluje volitelná ASP.NET a úloha vývoje webu.
Požadavky
- Sada .NET 8.0 SDK nebo novější
- Operační systém Windows
- Integrované vývojové prostředí .NET (IDE)
- Neváhejte používat visual studio
Vytvoření nového projektu
Pokud chcete vytvořit nový projekt Služby pracovního procesu pomocí sady Visual Studio, vyberte Soubor>Nový projekt>.... V dialogovém okně Vytvořit nový projekt vyhledejte "Pracovní služba" a vyberte šablonu pracovní služby. Pokud raději použijete .NET CLI, otevřete svůj oblíbený terminál v pracovním adresáři. Spusťte příkaz dotnet new
a nahraďte <Project.Name>
názvem požadovaného projektu.
dotnet new worker --name <Project.Name>
Další informace o příkazu projektu nové pracovní služby .NET CLI najdete viz dotnet new worker.
Rada
Pokud používáte Visual Studio Code, můžete z integrovaného terminálu spustit příkazy .NET CLI. Další informace naleznete v tématu Visual Studio Code: Integrovaný terminál.
Instalace balíčku NuGet
Pokud chcete spolupracovat s nativními službami Windows z implementací .NET IHostedService, budete muset nainstalovat balíček Microsoft.Extensions.Hosting.WindowsServices
NuGet.
Pokud chcete tuto instalaci nainstalovat ze sady Visual Studio, použijte dialogové okno Spravovat balíčky NuGet.... Vyhledejte "Microsoft.Extensions.Hosting.WindowsServices" a nainstalujte ho. Pokud byste raději použili .NET CLI, spusťte příkaz dotnet package add
:
dotnet package add Microsoft.Extensions.Hosting.WindowsServices
Další informace o příkazu pro přidání balíčku rozhraní .NET CLI najdete v tématu dotnet package add.
Po úspěšném přidání balíčků by teď soubor projektu měl obsahovat následující odkazy na balíčky:
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.3" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.3" />
</ItemGroup>
Aktualizace souboru projektu
Tento projekt pracovníka využívá referenční typy C# s možností hodnoty null. Pokud je chcete povolit pro celý projekt, aktualizujte soubor projektu odpovídajícím způsobem:
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<RootNamespace>App.WindowsService</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.3" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.3" />
</ItemGroup>
</Project>
Změny v předchozím souboru projektu přidávají uzel <Nullable>enable<Nullable>
. Další informace naleznete v tématu Nastavení nulovatelného kontextu.
Vytvoření služby
Přidejte do projektu novou třídu s názvem JokeService.csa nahraďte její obsah následujícím kódem jazyka C#:
namespace App.WindowsService;
public sealed class JokeService
{
public string GetJoke()
{
Joke joke = _jokes.ElementAt(
Random.Shared.Next(_jokes.Count));
return $"{joke.Setup}{Environment.NewLine}{joke.Punchline}";
}
// Programming jokes borrowed from:
// https://github.com/eklavyadev/karljoke/blob/main/source/jokes.json
private readonly HashSet<Joke> _jokes = new()
{
new Joke("What's the best thing about a Boolean?", "Even if you're wrong, you're only off by a bit."),
new Joke("What's the object-oriented way to become wealthy?", "Inheritance"),
new Joke("Why did the programmer quit their job?", "Because they didn't get arrays."),
new Joke("Why do programmers always mix up Halloween and Christmas?", "Because Oct 31 == Dec 25"),
new Joke("How many programmers does it take to change a lightbulb?", "None that's a hardware problem"),
new Joke("If you put a million monkeys at a million keyboards, one of them will eventually write a Java program", "the rest of them will write Perl"),
new Joke("['hip', 'hip']", "(hip hip array)"),
new Joke("To understand what recursion is...", "You must first understand what recursion is"),
new Joke("There are 10 types of people in this world...", "Those who understand binary and those who don't"),
new Joke("Which song would an exception sing?", "Can't catch me - Avicii"),
new Joke("Why do Java programmers wear glasses?", "Because they don't C#"),
new Joke("How do you check if a webpage is HTML5?", "Try it out on Internet Explorer"),
new Joke("A user interface is like a joke.", "If you have to explain it then it is not that good."),
new Joke("I was gonna tell you a joke about UDP...", "...but you might not get it."),
new Joke("The punchline often arrives before the set-up.", "Do you know the problem with UDP jokes?"),
new Joke("Why do C# and Java developers keep breaking their keyboards?", "Because they use a strongly typed language."),
new Joke("Knock-knock.", "A race condition. Who is there?"),
new Joke("What's the best part about TCP jokes?", "I get to keep telling them until you get them."),
new Joke("A programmer puts two glasses on their bedside table before going to sleep.", "A full one, in case they gets thirsty, and an empty one, in case they don’t."),
new Joke("There are 10 kinds of people in this world.", "Those who understand binary, those who don't, and those who weren't expecting a base 3 joke."),
new Joke("What did the router say to the doctor?", "It hurts when IP."),
new Joke("An IPv6 packet is walking out of the house.", "He goes nowhere."),
new Joke("3 SQL statements walk into a NoSQL bar. Soon, they walk out", "They couldn't find a table.")
};
}
readonly record struct Joke(string Setup, string Punchline);
Předchozí zdrojový kód služby vtipu odhaluje jedinou funkci, a to metodu GetJoke
. Jedná se o string
návratovou metodu, která představuje náhodný programovací vtip. Pole _jokes
s oborem třídy slouží k ukládání seznamu vtipů. Náhodný vtip je vybrán ze seznamu a vrácen.
Přepište třídu Worker
Nahraďte existující Worker
ze šablony následujícím kódem jazyka C# a přejmenujte soubor na WindowsBackgroundService.cs:
namespace App.WindowsService;
public sealed class WindowsBackgroundService(
JokeService jokeService,
ILogger<WindowsBackgroundService> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
while (!stoppingToken.IsCancellationRequested)
{
string joke = jokeService.GetJoke();
logger.LogWarning("{Joke}", joke);
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
}
catch (OperationCanceledException)
{
// When the stopping token is canceled, for example, a call made from services.msc,
// we shouldn't exit with a non-zero exit code. In other words, this is expected...
}
catch (Exception ex)
{
logger.LogError(ex, "{Message}", ex.Message);
// Terminates this process and returns an exit code to the operating system.
// This is required to avoid the 'BackgroundServiceExceptionBehavior', which
// performs one of two scenarios:
// 1. When set to "Ignore": will do nothing at all, errors cause zombie services.
// 2. When set to "StopHost": will cleanly stop the host, and log errors.
//
// In order for the Windows Service Management system to leverage configured
// recovery options, we need to terminate the process with a non-zero exit code.
Environment.Exit(1);
}
}
}
V předchozím kódu se JokeService
vloží spolu s ILogger
. Obě jsou k dispozici pro třídu jako pole. V metodě ExecuteAsync
služba pro vtipy požádá o vtip a zapíše ho do protokolu. V tomto případě je protokolovací nástroj implementován protokolem událostí systému Windows - Microsoft.Extensions.Logging.EventLog.EventLogLoggerProvider. Protokoly se zapisují do Prohlížeče událostí a jsou k dispozici pro zobrazení.
Poznámka
Ve výchozím nastavení je závažnost protokolu událostíWarning. To je možné nakonfigurovat, ale pro demonstrační účely WindowsBackgroundService
logy pomocí rozšiřovací metody LogWarning. Pokud chcete konkrétně cílit na úroveň EventLog
, přidejte do nastavení aplikace položku. {Environment}.jsonnebo zadejte hodnotu EventLogSettings.Filter.
{
"Logging": {
"LogLevel": {
"Default": "Warning"
},
"EventLog": {
"SourceName": "The Joke Service",
"LogName": "Application",
"LogLevel": {
"Microsoft": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
}
Další informace o konfiguraci úrovní protokolu naleznete v tématu Zprostředkovatelé protokolování v .NET: Konfigurace protokolu událostí systému Windows.
Přepište třídu Program
Nahraďte obsah souboru šablony Program.cs následujícím kódem jazyka C#:
using App.WindowsService;
using Microsoft.Extensions.Logging.Configuration;
using Microsoft.Extensions.Logging.EventLog;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddWindowsService(options =>
{
options.ServiceName = ".NET Joke Service";
});
LoggerProviderOptions.RegisterProviderOptions<
EventLogSettings, EventLogLoggerProvider>(builder.Services);
builder.Services.AddSingleton<JokeService>();
builder.Services.AddHostedService<WindowsBackgroundService>();
IHost host = builder.Build();
host.Run();
Metoda rozšíření AddWindowsService
nakonfiguruje aplikaci tak, aby fungovala jako služba Systému Windows. Název služby je nastavený na ".NET Joke Service"
. Hostovaná služba je registrovaná pro injektování závislostí.
Další informace o registraci služeb naleznete v tématu Injektáž závislostí v rozhraní .NET.
Publikování aplikace
Pokud chcete vytvořit aplikaci .NET Worker Service jako službu systému Windows, doporučujeme aplikaci publikovat jako spustitelný soubor. Je méně náchylné k chybám mít samostatný spustitelný soubor, protože neexistují žádné závislé soubory kolem systému souborů. Můžete ale zvolit jiný způsob publikování, což je naprosto přijatelné, pokud vytvoříte *.exe soubor, který může být cílem Správce řízení služeb systému Windows.
Důležitý
Alternativním přístupem k publikování je sestavení *.dll (místo *.exe) a při instalaci publikované aplikace pomocí Správce řízení služeb systému Windows delegujete úkol na rozhraní příkazového řádku .NET a předáte knihovnu DLL. Další informace najdete v tématu .NET CLI: příkaz dotnet.
sc.exe create ".NET Joke Service" binpath= "C:\Path\To\dotnet.exe C:\Path\To\App.WindowsService.dll"
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<RootNamespace>App.WindowsService</RootNamespace>
<OutputType>exe</OutputType>
<PublishSingleFile Condition="'$(Configuration)' == 'Release'">true</PublishSingleFile>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.3" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.3" />
</ItemGroup>
</Project>
Předchozí zvýrazněné řádky souboru projektu definují následující chování:
-
<OutputType>exe</OutputType>
: Vytvoří konzolovou aplikaci. -
<PublishSingleFile Condition="'$(Configuration)' == 'Release'">true</PublishSingleFile>
: Povolí publikování s jedním souborem. -
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
: Určuje RID zwin-x64
. -
<PlatformTarget>x64</PlatformTarget>
: Zadejte procesor 64bitové cílové platformy.
Pokud chcete aplikaci publikovat ze sady Visual Studio, můžete vytvořit profil publikování, který je trvalý. Profil publikování je založený na jazyce XML a má příponu souboru .pubxml. Visual Studio používá tento profil k implicitní publikování aplikace, zatímco pokud používáte .NET CLI, musíte explicitně zadat profil publikování, který se má použít.
Klikněte pravým tlačítkem myši na projekt v průzkumníku řešení, a vyberte Publikovat. Potom vyberte Přidat publikační profil a vytvořte profil. V dialogovém okně Publikovat vyberte složku jako svou cílovou.
Ponechte základní Umístění, poté vyberte Dokončit. Po vytvoření profilu vyberte Zobrazit všechna nastavenía ověřte nastavení profilu.
Ujistěte se, že jsou zadána následující nastavení:
- režim nasazení: samostatné
- Vytvořit jeden soubor: zaškrtnuto
- povolení kompilace ReadyToRun: zaškrtnuto
- Oříznutí nepoužívaných sestavení (ve verzi Preview): nezaškrtnuto
Nakonec vyberte Publikovat. Aplikace se zkompiluje a výsledný soubor .exe se publikuje do výstupního adresáře /publish.
Případně můžete k publikování aplikace použít rozhraní příkazového řádku .NET:
dotnet publish --output "C:\custom\publish\directory"
Další informace najdete v tématu dotnet publish
.
Důležitý
Pokud se v .NET 6 pokusíte aplikaci ladit pomocí nastavení <PublishSingleFile>true</PublishSingleFile>
, nebudete moct aplikaci ladit. Další informace najdete v tématu Nelze se připojit k CoreCLR při ladění aplikace .NET 6 PublishSingleFile.
Vytvoření služby systému Windows
Pokud neznáte powershell a raději byste pro službu vytvořili instalační program, přečtěte si téma Vytvoření instalačního programu služby systému Windows. V opačném případě vytvořte službu Systému Windows pomocí nativního příkazu Windows Service Control Manager (sc.exe) create. Spusťte PowerShell jako správce.
sc.exe create ".NET Joke Service" binpath= "C:\Path\To\App.WindowsService.exe"
Rada
Pokud potřebujete změnit kořenový adresář obsahu v konfiguraci hostitele, můžete jej předat jako argument příkazové řádky při specifikaci binpath
:
sc.exe create "Svc Name" binpath= "C:\Path\To\App.exe --contentRoot C:\Other\Path"
Zobrazí se výstupní zpráva:
[SC] CreateService SUCCESS
Pro více informací viz sc.exe vytvoření.
Konfigurace služby systému Windows
Po vytvoření služby ji můžete volitelně nakonfigurovat. Pokud máte výchozí nastavení služby v pořádku, přejděte do části Ověření funkčnosti služby.
Služby Windows poskytují možnosti konfigurace obnovení. Aktuální konfiguraci můžete dotazovat pomocí příkazu sc.exe qfailure "<Service Name>"
(kde <Service Name>
je název vaší služby) a přečíst aktuální hodnoty konfigurace obnovení:
sc qfailure ".NET Joke Service"
[SC] QueryServiceConfig2 SUCCESS
SERVICE_NAME: .NET Joke Service
RESET_PERIOD (in seconds) : 0
REBOOT_MESSAGE :
COMMAND_LINE :
Příkaz zobrazí výstup konfigurace obnovení, což jsou výchozí hodnoty, protože ještě nejsou nakonfigurované.
Pokud chcete nakonfigurovat obnovení, použijte sc.exe failure "<Service Name>"
, kde <Service Name>
je název vaší služby:
sc.exe failure ".NET Joke Service" reset= 0 actions= restart/60000/restart/60000/run/1000
[SC] ChangeServiceConfig2 SUCCESS
Rada
Pokud chcete nakonfigurovat možnosti obnovení, musí být vaše relace terminálu spuštěna s oprávněními správce.
Po úspěšné konfiguraci můžete znovu zadat dotaz na hodnoty pomocí příkazu sc.exe qfailure "<Service Name>"
:
sc qfailure ".NET Joke Service"
[SC] QueryServiceConfig2 SUCCESS
SERVICE_NAME: .NET Joke Service
RESET_PERIOD (in seconds) : 0
REBOOT_MESSAGE :
COMMAND_LINE :
FAILURE_ACTIONS : RESTART -- Delay = 60000 milliseconds.
RESTART -- Delay = 60000 milliseconds.
RUN PROCESS -- Delay = 1000 milliseconds.
Zobrazí se nakonfigurované hodnoty restartování.
Možnosti obnovy služby a instance .NET BackgroundService
V rozhraní .NET 6 do .NET byly přidány nové chování zpracování výjimek hostování,. Výčet BackgroundServiceExceptionBehavior byl přidán do oboru názvů Microsoft.Extensions.Hosting
a slouží k určení chování služby při vyvolání výjimky. V následující tabulce jsou uvedené dostupné možnosti:
Možnost | Popis |
---|---|
Ignore | Ignorovat výjimky vyvolané v BackgroundService . |
StopHost | Při vyvolání neošetřené výjimky se IHost zastaví. |
Výchozí chování před rozhraním .NET 6 je Ignore
, což vedlo k zombie procesům (běžící proces, který nic nedělal). U .NET 6 je výchozí chování StopHost
, což vede k zastavení hostitele při vyvolání výjimky. Ale zastaví se čistě, což znamená, že správce služeb systému Windows nerestartuje službu. Chcete-li správně povolit restartování služby, můžete volat Environment.Exit s nenulovým ukončovacím kódem. Zvažte následující zvýrazněný blok catch
:
namespace App.WindowsService;
public sealed class WindowsBackgroundService(
JokeService jokeService,
ILogger<WindowsBackgroundService> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
while (!stoppingToken.IsCancellationRequested)
{
string joke = jokeService.GetJoke();
logger.LogWarning("{Joke}", joke);
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
}
catch (OperationCanceledException)
{
// When the stopping token is canceled, for example, a call made from services.msc,
// we shouldn't exit with a non-zero exit code. In other words, this is expected...
}
catch (Exception ex)
{
logger.LogError(ex, "{Message}", ex.Message);
// Terminates this process and returns an exit code to the operating system.
// This is required to avoid the 'BackgroundServiceExceptionBehavior', which
// performs one of two scenarios:
// 1. When set to "Ignore": will do nothing at all, errors cause zombie services.
// 2. When set to "StopHost": will cleanly stop the host, and log errors.
//
// In order for the Windows Service Management system to leverage configured
// recovery options, we need to terminate the process with a non-zero exit code.
Environment.Exit(1);
}
}
}
Ověření funkčnosti služby
Pokud chcete zobrazit aplikaci vytvořenou jako službu Systému Windows, otevřete Services . Vyberte klávesu Windows (nebo Ctrl + Esc) a vyhledejte "Služby". V aplikaci Services byste měli být schopni vaši službu najít podle jejího názvu.
Důležitý
Ve výchozím nastavení nemůžou běžní uživatelé (bez oprávnění správce) spravovat služby systému Windows. Pokud chcete ověřit, že tato aplikace funguje podle očekávání, budete muset použít účet správce.
Pokud chcete ověřit, že služba funguje podle očekávání, musíte:
- Spusťte službu
- Zobrazení protokolů
- Zastavení služby
Důležitý
Pokud chcete ladit aplikaci, ujistěte se, že se nepokoušíte ladit spustitelný soubor, který je aktivně spuštěný v rámci procesu Windows Services.
Spuštění služby systému Windows
Ke spuštění služby systému Windows použijte příkaz sc.exe start
:
sc.exe start ".NET Joke Service"
Zobrazí se výstup podobný následujícímu:
SERVICE_NAME: .NET Joke Service
TYPE : 10 WIN32_OWN_PROCESS
STATE : 2 START_PENDING
(NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x7d0
PID : 37636
FLAGS
Stav služby přejde ze stavu START_PENDING
do stavu , spuštěno.
Zobrazení protokolů
Pokud chcete zobrazit protokoly, otevřete prohlížeče událostí. Vyberte klávesu Windows (nebo Ctrl + Esc) a vyhledejte "Event Viewer"
. Vyberte uzel Prohlížeč událostí (místní)>Protokoly systému Windows>Aplikace. Měli byste vidět položku na úrovni varování , která má zdroj odpovídající oboru názvů aplikací. Poklikejte na položku, nebo klikněte pravým tlačítkem myši a vyberte Vlastnosti události, abyste zobrazili podrobnosti.
Po zobrazení protokolů v protokolu událostí byste měli službu zastavit. Je navržený tak, aby protokoloval náhodný vtip jednou za minutu. Jedná se o úmyslné chování, ale není praktické pro produkční služby.
Zastavení služby systému Windows
Pokud chcete službu Systému Windows zastavit, použijte příkaz sc.exe stop
:
sc.exe stop ".NET Joke Service"
Zobrazí se výstup podobný následujícímu:
SERVICE_NAME: .NET Joke Service
TYPE : 10 WIN32_OWN_PROCESS
STATE : 3 STOP_PENDING
(STOPPABLE, NOT_PAUSABLE, ACCEPTS_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x0
Stav služby se změní z STOP_PENDING
na Zastaveno.
Odstranění služby systému Windows
Pokud chcete odstranit službu Systému Windows, použijte příkaz delete nativního správce řízení služeb systému Windows (sc.exe). Spusťte PowerShell jako správce.
Důležitý
Pokud služba není ve stavu Zastaveno, nebude okamžitě odstraněna. Před vydáním příkazu delete se ujistěte, že je služba zastavená.
sc.exe delete ".NET Joke Service"
Zobrazí se výstupní zpráva:
[SC] DeleteService SUCCESS
Další informace najdete v tématu sc.exe odstranění.
Viz také
- Vytvoření instalačního programu služby systému Windows
- Služby pracovníka v rozhraní .NET
- Vytvořte službu fronty
-
Použití služeb s vymezeným oborem v rámci
BackgroundService
-
Implementovat rozhraní
IHostedService