踩坑 Windows 服務來宿主 .NET 程式

2022-09-02 12:03:12

本文所指的 .NET 程式為 .NET6 的程式。因為 .NET 的版本更新很快,所以方式、方法也有變化,所以網上搜到的方法有些也過時了。以下是最近我實踐下來的一點心得(坑)。
上一篇說到 不安裝執行時執行 .NET 程式 後我們的程式已經只有一個 dll/exe 了,但是在 windows 上執行的時候會是一個控制檯程式,很容易人不小心關閉了。所以想著把我們的程式部署成 windows 服務,這樣不會誤關,重啟伺服器的時候也會自動啟動。所以最近折騰了一下把 .NET 程式,特別是 ASP.NET Core 程式部署為 windows 服務。本來以為網上隨便搜一搜就很容易,事實上沒想得這麼美好。

Worker Service

如果你的服務只想執行一些後臺任務,比如定時任務,並不提供網站的服務。那麼使用 Worker service 專案模板新建一個專案是最合適的。

新建完專案後使用 nuget 安裝:

Microsoft.Extensions.Hosting.WindowsServices

修改 program.cs 檔案

using WorkerService1;

IHost host = Host.CreateDefaultBuilder(args)
    .UseWindowsService()
    .ConfigureServices(services =>
    {
        services.AddHostedService<Worker>();
    })
    .Build();

await host.RunAsync();

其中就只新增一句 UseWindowsService 就可以了。
編譯之後使用 sc 命令就可以註冊為服務了。

sc create "wsTest" binPath=wsTest.exe
sc start "wsTest"
pause

我們把它寫成一個 bat 檔案方便執行。使用管理許可權執行這個 bat 檔案服務會被註冊並且直接執行:

ASP.NET Core

以上嘗試了一下 worker service 模式註冊為服務,感覺簡單的很。但是下面把 ASP.NET Core 程式註冊為服務的時候就沒那麼簡單啦。我查了一些文章,寫的時間有些早了,所以還是安照微軟官方的檔案 Host ASP.NET Core in a Windows Service 的提示來操作。這篇文章雖然叫 Host ASP.NET Core in a Windows Service ,但其實裡面的內容說的是上面的 worker service 。當按照上面的步驟嘗試把 asp.net core 程式部署為服務的時候死活起不來,一直報未找到檔案的異常。
我們新建一個最簡單的 asp.net core 程式來演示一下:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Host.UseWindowsService();

var app = builder.Build();

app.UseStaticFiles();
app.UseRouting();
await app.RunAsync();

這是一個最簡單的 asp.net core razor pages 程式。我們按照檔案安裝擴充套件包:

Microsoft.Extensions.Hosting.WindowsServices

然後呼叫 UseWindowsService 方法

builder.Host.UseWindowsService();

同樣使用 sc 命令註冊為服務並執行它。很遺憾它會報錯。

經過多種嘗試方法都不行。最後翻了一下微軟倉庫裡的 sample 程式碼才發現原來他們自己的 sample 程式碼裡多了幾行程式碼 。

注意下面的程式碼了啊 !!!

在構建 builder 的時候需要多傳幾個引數:

var options = new WebApplicationOptions
{
    Args = args,
    ContentRootPath = WindowsServiceHelpers.IsWindowsService() ? AppContext.BaseDirectory : default
};
var builder = WebApplication.CreateBuilder(options);

只要使用這段程式碼來構建 builder ,服務就可以順利的執行起來。根據以往的經驗,可能還是作為服務執行的時候程式根目錄的問題,通過以上方法來指定 AppContext.BaseDirectory 來作為程式的根目錄,不然就有可能被定位到 system32 目錄下。
吐槽。。。雖然解決方案很簡單,但是微軟沒有在檔案裡把這麼關鍵的程式碼給些出來,著實有點坑啊,辛虧現在程式碼都在 github 上,很容易翻到,不然得折騰死人。

PS

就在今天2022/09/02微軟的英文檔案已經更新了範例程式碼,中文版還沒更新:
https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/windows-service?view=aspnetcore-6.0&tabs=visual-studio#app-configuration-1

關注我的公眾號一起玩轉技術