使用 .NET Core 製作 Windows Service

Posted by Ryan Tseng on 2018-10-25

上次的文章介紹了 TopShelf 這個框架,能夠幫助我們使用很簡單的方式建構出一個 Windows Service,這次我們要使用 .NET Core 內建的功能,並搭配 Service Control (sc.exe) 來幫我們把 Application 註冊到 Windows Service 當中

環境

  • Windows 10
  • Rider 2018.2.3
  • .NET Core 2.1

步驟

我們要使用的是 .NET Core MVC (Model-View-Controller) 的專案,大家對 .NET MVC 專案的印象都停留在他必須要佈署到 IIS 上面才可以運行,但其實 .NET Core 本身可以透過 Microsoft.AspNetCore.Hosting.WindowsServices 讓 Windows Service 來 Host 整個服務。

建立專案

將平台改為 win7-x64 (因為要執行在 Windows 上)

1
2
3
4
5
6
7
8
9
10
11
12
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<RuntimeIdentifier>win7-x64</RuntimeIdentifier>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App"/>
</ItemGroup>

</Project>

安裝套件

安裝 Microsoft.AspNetCore.Hosting.WindowsServices 套件,讓服務可以裝載到 Windows Service 中

微調程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void Main(string[] args)
{
// CreateWebHostBuilder(args).Build().Run();
CreateWebHostBuilder(args).Build().RunAsService();
}

// public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
// WebHost.CreateDefaultBuilder(args)
// .UseStartup<Startup>();

public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
var pathToContentRoot = Path.GetDirectoryName(pathToExe);

return WebHost.CreateDefaultBuilder(args)
.UseContentRoot(pathToContentRoot)
.UseStartup<Startup>();
}

發行 .NET Core 應用程式

1
dotnet publish --configuration Release

我們可以在 bin/Release/netcoreapp2.1/win7-x64 當中找到剛剛 publish 指令產生的檔案

使用 sc.exe

為了要把這個服務註冊到 Windows Service 中,我們需要藉助 sc.exe (Service Control) 來幫忙,首先要找到我們 publish 的安裝檔位置,並且使用以下指令來建立 Windows Service

1
sc create MySvc binPath= "E:\Temp\deploy\NetCoreWinSvcLab.exe"

注意 binPath="E:\Temp\deploy\NetCoreWinSvcLab.exe" 之間一定要有空格

啟動 Service

1
sc start MySvc

查詢 Service 狀態

1
sc query MySvc

可以看到有沒有在執行

同樣的也可以透過 Windows 提供的 Windows 服務管理員介面來看服務是不是有執行起來

因為我們這次註冊的服務是一個 Web Application,所以也可以透過網頁來看服務是不是有執行

1
http://localhost:5000/

停止 Service

1
sc stop MySvc

解除安裝 Service

注意在解除安裝之前要先讓服務處於 STOPPED 狀態

1
sc delete MySvc

處理事件 (啟動、停止)

文章看到現在其實會發現把 Application 註冊到 Windows Service 本身不是特別的麻煩,但會發現他沒有像是 TopShelf 包裝好的那些類似生命週期的事件,不過我們可以透過繼承 WebHostService 類別來覆寫 OnStarting, OnStarted, OnStopping, OnStopped 原本的行為

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
 public static class CustomWebHostServiceExtension
{
public static void RunAsCustomService(this IWebHost host)
{
var webHostService = new CustomWebHostService(host);
ServiceBase.Run(webHostService);
}
}

public class CustomWebHostService : WebHostService
{
public CustomWebHostService(IWebHost host) : base(host)
{
}

// 這邊只是為了要觀察這些事件是不是真的有在服務執行、關閉的時候觸發所寫的 Log
protected override void OnStarting(string[] args)
{
File.AppendAllText("E://log.txt", $"OnStarting() [{DateTime.Now}] {Environment.NewLine}");
}

protected override void OnStarted()
{
File.AppendAllText("E://log.txt", $"OnStarted() [{DateTime.Now}] {Environment.NewLine} ");
}

protected override void OnStopping()
{
File.AppendAllText("E://log.txt", $"OnStopping() [{DateTime.Now}] {Environment.NewLine}");
}

protected override void OnStopped()
{
File.AppendAllText("E://log.txt", $"OnStopped() [{DateTime.Now}] {Environment.NewLine}");
}
}

然後修改一下 Program.cs,改成使用剛剛定義好的 RunAsCustomService

1
2
3
4
5
public static void Main(string[] args)
{
// CreateWebHostBuilder(args).Build().RunAsService();
CreateWebHostBuilder(args).Build().RunAsCustomService();
}

再來依照前面的 command line 語法啟動/停止 service

並觀察是否有出現 log.txt 在我們預期的路徑,以及 log 內容是不是真的依照這個生命週期觸發

1
2
3
4
5
6
7
//> sc start MySvc
OnStarting() [2018/10/24 下午 05:16:33]
OnStarted() [2018/10/24 下午 05:16:34]

//> sc stop MySvc
OnStopping() [2018/10/24 下午 05:16:36]
OnStopped() [2018/10/24 下午 05:16:36]

結論

當初使用 TopShelf 的時候覺得很方便,但是畢竟還是依附在寫好的框架底下,不過必須說,TopSHelf 畢竟還是已經推出了一段時間的框架,功能上其實算是完整,可以透過程式或是命令列參數來調整設定,但 .NET Core MVC + sc.exe 這個組合,有很多設定可能要自己處理,相對來說比較沒有這麼方便。

不過單純就整個開專案到安裝 Service 的步驟,整體操作起來我認為沒有到非常複雜,各位讀者可以照著本文或是下面參考資料的文章做一次,將自己的 Application 裝載到 Windows 服務當中來執行。

參考資料