[C#] C#でサービスを作る(.NET5.0)

# Windowsサービス関連記事

https://tera1707.com/entry/2022/02/06/144447#WindowsService

# やりたいこと

以前、[C++やC#(.NET Framework)でサービスを作った](https://tera1707.com/entry/2022/02/06/144447#WindowsService)が、C#(.NET5)だとまた違う作り方があるらしい。
それをやってみる。

# やり方

.NET5の「ワーカー サービス」のテンプレートを使用する。

# 前提

VisualStudio2019 / .NET5.0

# 手順

### ■プロジェクト作成

新しいプロジェクトの作成で「ワーカー サービス」を選択しプロジェクトを作成する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/19fccc39-98ad-fbc8-7a20-2976257f82df.png)
プロジェクトを作成したら、こういう構成になっている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/fea17230-f450-6ee3-3e14-bc0432660e45.png)

### ■Microsoft.Extensions.Hosting.WindowsServices をインストール

作成したプロジェクト(ここでは**「MyService」**)の「Nugetの管理」を開き、「Microsoft.Extensions.Hosting.WindowsServices」のパッケージを検索、インストールする。[^※1]
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/493eccdf-cf9e-3391-0353-14db9ed81ac3.png)
※「Microsoft.Extensions.Hosting」のほうは、プロジェクトのテンプレートに元からインストールされているもの。

[^※1]: 今回.NET5で試したので、「ワーカーサービス」のテンプレートに元から入っている6.0.0ではなく5.0.0を使用。`Microsoft.Extensions.Hosting`のバージョンに合わせて`Microsoft.Extensions.Hosting.WindowsServices`のバージョンを選ぶ必要あり。

### ■IHostBuilder CreateHostBuilder(string[] args)を修正

`Host.CreateDefaultBuilder(args)・・・`している部分の一番後ろに、
`.UseWindowsService();`を付ける。

``` Program.cs
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace MyService
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddHostedService<Worker>();
                }).UseWindowsService();// ★★←これを追加
    }
}
```

「これを追加」の部分以外は、テンプレートで生成されたコードそのまま。

これで、このプロジェクトをビルドしてできる「MyService.exe」を、サービスとして動かす準備はできた。
ビルドすると、標準で`MyService\MyService\bin\Debug\net5.0` のようなフォルダにexeができる。

### ■サービスとしてPCに入れる

下記のバッチファイルを作成し、ビルドしてできたexeと同じフォルダに置き、管理者で実行する。今回は、サービス名＝`MyService`とした。
(個別にscコマンドでインストールを行ってもOK。何度も同じコマンド打つのが面倒なので今回はバッチをつくった)

``` InstallMyService.bat
@echo off
set SERVICENAME="MyService"
set EXEPATH="%~dp0MyService.exe"

rem 2回目以降の実験のために、まずサービスを止めて、古いのを一度消す
net stop %SERVICENAME%
sc delete %SERVICENAME%

rem サービスインストール
sc create %SERVICENAME% binPath= %EXEPATH%

rem サービス開始
net start %SERVICENAME%
```

これで、テンプレートのまんまの、1秒おきにログを取るだけのサービスが動いた。

### ■サービスで行いたい処理を実装する

`Worker.cs`の`Task ExecuteAsync(CancellationToken stoppingToken)`が、サービスの処理を書くためのメソッド。
ここに、やりたい処理を書く。

``` Worker.cs
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace MyService
{
    public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;

        public Worker(ILogger<Worker> logger)
        {
            _logger = logger;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                // ★★ここに、やりたいことを書く★★
                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
                await Task.Delay(1000, stoppingToken);
            }
        }
    }
}

```

コメント以外は、テンプレートで生成されたコードそのまま。
下の「参考」に挙げたようなサイトでは、ここにHTTPサーバーの機能を記述する、などされていた。

### ■サービスの開始時や終了時になにかしたいとき

BackgroundServiceクラスの「StartAsync()」「StopAsync()」をオーバーライドし、そこに処理を書く。

``` startとstopを追加したWorker.cs
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace MyService
{
    public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;

        public Worker(ILogger<Worker> logger)
        {
            Thread.Sleep(10000);
            _logger = logger;
        }

        public override Task StartAsync(CancellationToken cancellationToken)
        {
            // ここにサービス開始時の処理を書く
            return base.StartAsync(cancellationToken);
        }

        public override Task StopAsync(CancellationToken cancellationToken)
        {
            // ここにサービス終了時の処理を書く
            return base.StopAsync(cancellationToken);
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
                await Task.Delay(1000, stoppingToken);
            }
        }
    }
}
```

### ■サービスの「スタートアップの種類」や「説明」を変えたいとき

この辺を変えたいとき。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/735024ab-4509-8eed-ad93-4b47e72f89b4.png)

サービスをインストールするときのscコマンドで変更する。

例えばこうすると

```
rem サービスインストール
sc create %SERVICENAME% binPath= %EXEPATH% start= auto
sc description %SERVICENAME% setumeibun
```

こうなる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/ee6c3e10-49ac-ff7d-a932-465a6b2f7203.png)


# WorkerServiceでの、サービスの「一時停止」「再開」はどうやる？

「一時停止」「再開」は、WOrkerServiceを使ったWindowsサービスでは存在しないっぽい。→つまり、できない。
下記記事では、昔のwindowsサービスから今のWorkerServiceに乗り換える場合は、サービスの再設計が必要、とある。

https://stackoverflow.com/questions/70011941/onpause-oncontinue-on-worker-service

# 参考

Microsoft.Extensions.Hosting.WindowsServices のパッケージのインストールが必要。
また「.UseWindowsService();」の追記が必要。

http://kawakawa2000.jugem.jp/?eid=405

動画でサービスの作り方を説明（英語・短い）

https://www.youtube.com/watch?v=1veEL-oQHXM

動画でサービスの作り方を説明（英語・長いけど詳しい）

https://www.youtube.com/watch?v=PzrTiz_NRKA

「WorkerService」を「WindowsService」にする
https://youtu.be/PzrTiz_NRKA?t=1774

MS公式

https://docs.microsoft.com/ja-jp/dotnet/core/extensions/windows-service

https://www.stevejgordon.co.uk/what-are-dotnet-worker-services

scコマンド(Create)

https://windows.command-ref.com/cmd-sc.html

scコマンドDescriptionn)

https://windows.command-ref.com/cmd-sc-description.html