---
# System prepended metadata

title: 使用.Net6建立Windows Service
tags: [.Net 6, Windows Service, Worker Serivce]

---

###### 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`。
![](https://i.imgur.com/8Mc2sNV.png)

在設定完專案名稱及位置後，按下一步。
![](https://i.imgur.com/Z1nJMkm.png)

架構請選擇`.Net6.0(長期支援)`，按建立。
![](https://i.imgur.com/ovdpLgi.png)

## 安裝Nuget套件
請在管理Nuget套件中安裝`Microsoft.Extensions.Hosting.WindowsServices`
![](https://i.imgur.com/NAfXz8a.png)
## 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 成功
```
此時開啟`服務`將會看到你的服務已經被建立了。
![](https://i.imgur.com/6auP42I.png)
## 啟動 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              :
```
此時開啟`服務`將會看到你的服務已經被啟動了。
![](https://i.imgur.com/T8s3D5W.png)
## 停止 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
```
此時開啟`服務`將會看到你的服務已經被停止了。
![](https://i.imgur.com/THQhaAu.png)
## 刪除 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)