###### tags: `.Net 6`, `Worker Serivce`, `Windows Service`
[toc]
# 使用.Net6建立Windows Service
本篇主要示範如何使用`.Net6`中的`Worker Service`專案來建立一個可於背景執行的`Windows Service`,來替我們做一些需要定期執行或長期運作的服務。
本篇的資料來源來自[黑大的文章](https://blog.darkthread.net/blog/net6-windows-service/)及[微軟官方的說明文件](https://docs.microsoft.com/zh-tw/dotnet/core/extensions/windows-service?WT.mc_id=DOP-MVP-37580),想深入了解的話記得點進去看看。
## 建立專案
首先要來建立專案,請選擇`Worker Service`。

在設定完專案名稱及位置後,按下一步。

架構請選擇`.Net6.0(長期支援)`,按建立。

## 安裝Nuget套件
請在管理Nuget套件中安裝`Microsoft.Extensions.Hosting.WindowsServices`

## Program重寫類別
請修改`Program.cs`的內容,如下:
```csharp=
using TCService;
IHost host = Host.CreateDefaultBuilder(args)
.UseWindowsService(options =>
{
options.ServiceName = "TCService";
})
.ConfigureServices(services =>
{
// 如果你有其他service類別,在這裡做相依注入,例如以下:
// services.AddSingleton<JokeService>();
services.AddHostedService<Worker>();
})
.Build();
await host.RunAsync();
```
* 第3行的`UseWindowsService`擴充方法會將應用程式設定為以`Windows服務`的形式運作。 服務名稱會設定為`TCService`。
* 在`ConfigureServices`中將需要的服務注入,如第11行被註解的程式碼所示。
## 避免service 安裝起來在奇怪的路徑
如果有產log和db會發現service 總是產在奇怪的路徑
"user 底下 appdata ....產生的亂數路徑"
而且每次安裝更新還不一定會在相同路徑底下。
這時候,好好的配置路徑就非常重要了。
1. 一樣在Program.cs底下配置
```csharp=
IHost host = Host.CreateDefaultBuilder(args)
.UseWindowsService()
// 配置這一行 告訴程式運行的和產生檔案的BasePath
.ConfigureAppConfiguration((hostContext, config) => {
string pathToExe = Process.GetCurrentProcess().MainModule.FileName;
string dir = Path.GetDirectoryName(pathToExe);
config.SetBasePath(dir);
})
```
## 讓appSetting 的參數像model 一樣使用吧
1. appSetting的設定
```json=
{
// 把WCF轉成 model使用
"WCF": {
"LineToken": "hVPkk2PPfS19KjgNJTo52pOtKyHJi0O7Iy0e08Wlx0f",
// 用逗號分隔
"ServiceList": "BthAvctpSvc,Fax,TTIAService"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
```
2. 建個model 檔
```csharp=
public class WorkerOptions
{
public string LineToken { get; set; }
public string ServiceList { get; set; }
}
```
3. 在Program.cs 注入
```csharp=
IHost host = Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker>();
IConfiguration configuration = hostContext.Configuration;
// 這邊注入
WorkerOptions options = configuration.GetSection("WCF").Get<WorkerOptions>();
services.AddHostedService<Worker>();
})
.Build();
```
4. 使用方法
```csharp=
public WorkerOptions options;
public Worker(IServiceProvider provider)
{
_provider = provider;
options = _provider.GetService<IConfiguration>().GetSection("WCF").Get<WorkerOptions>();
}
```
## 想要把Log4Net配置到worker service
1. **注意是安裝aspNetCore 版本的**
nuget 安裝Microsoft.Extensions.Logging.Log4Net.AspNetCore
2. 注入到Program.cs
```csharp=
IHost host = Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureAppConfiguration((hostContext, config) => {
string pathToExe = Process.GetCurrentProcess().MainModule.FileName;
string dir = Path.GetDirectoryName(pathToExe);
config.SetBasePath(dir);
})
// 加入這邊
.ConfigureLogging((hostingContext, config) =>
{
// 一樣先告知程式 初始目錄位置
string pathToExe = Process.GetCurrentProcess().MainModule.FileName;
string dir = Path.GetDirectoryName(pathToExe);
var file = string.Format("{0}{1}", dir, @"\log4net.config");
// 載入檔案
config.AddLog4Net(file, true);
// 選擇最低要輸出的層級 這邊是debug 就不輸出了
config.SetMinimumLevel(LogLevel.Debug);
})
```
3. 使用方法
在需要的地方寫上這一行,記得注入provider
```csharp=
_provider.GetService<ILogger<Worker>>().LogError($"ExecuteAsync {ex.Message}");
```
## 建立 Windows 服務
做完上述修改即可將專案發佈到資料夾,接著要將專案轉為`Windows Service`運行,請==以系統管理員身分執行命令提示字元==,並使用原生 Windows服務控制管理員的 (sc.exe) create 命令。
```
sc.exe create "TCService" binpath="C:\Path\To\App.WindowsService.exe"
```
在執行完上述命令後,若成功應該會回覆
```
[SC] CreateService 成功
```
此時開啟`服務`將會看到你的服務已經被建立了。

## 啟動 Windows 服務
接著你就可以直接使用上述這個`服務`介面進行啟動服務
當然也可以使用命令啟動服務:
```
sc.exe start "TCService"
```
若成功啟動應該會看到類似下方的回覆:
```
SERVICE_NAME: TCService
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 : 33156
FLAGS :
```
此時開啟`服務`將會看到你的服務已經被啟動了。

## 停止 Windows 服務
接著你就可以直接使用上述這個`服務`介面停止服務
當然也可以使用命令停止服務:
```
sc.exe stop "TCService"
```
若成功停止應該會看到類似下方的回覆:
```
SERVICE_NAME: TCService
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
```
此時開啟`服務`將會看到你的服務已經被停止了。

## 刪除 Windows 服務
使用命令刪除服務:
```
sc.exe delete "TCService"
```
若成功刪除應該會看到類似下方的回覆:
```
[SC] DeleteService 成功
```
此時開啟`服務`將會看到你的服務已經不見了。
## 參考連結
* [黑暗執行緒-使用 .NET 6 開發 Windows Service](https://blog.darkthread.net/blog/net6-windows-service/)
* [微軟-Create a Windows Service using BackgroundService](https://docs.microsoft.com/zh-tw/dotnet/core/extensions/windows-service?WT.mc_id=DOP-MVP-37580)