###### 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)