Windows Serverでプロセスをサービス化する(pythonのsupervisorっぽく)

Windows Serverで長期間動作し続けるサービスのためにプロセスを監視して落ちたら自動で再起動するServiceの作り方です。
pythonのsupervisorのようなもの。

# Serviceの作り方

ServiceBaseを継承し、OnStart()とOnStop()のoverrideにやりたい事を実装するといいようです。
プロセスの起動、落ちてるかの確認と再起動、プロセスの停止を実装してみます。

```csharp
using System.Diagnostics;
using System.ServiceProcess;
using System.Threading.Tasks;

namespace Suporvisor
{
    class Suporvisor: ServiceBase
    {
        public static void Main()
        {
            var service = new Suporvisor();
            ServiceBase.Run(service);
            return;
        }

        const int checkIntervalMillseconds = 1000;
        const string exePath = @"C:\Supervisor\Program.exe";
        readonly object lockObject = new object();
        Process process;
        Task checker;

        public Suporvisor()
        {
            AutoLog = true;
            CanShutdown = true;
            CanStop = true;
            ServiceName = "Supervisor";
        }

        protected override void OnStart(string[] args)
        {
            log("OnStart");
            lock (lockObject)
            {
                spawnWithoutLock();
            }
            checker = check();
        }

        protected override void OnStop()
        {
            log("OnStop");
            stop();
            checker.Wait();
            ExitCode = 0;
        }

        async Task check()
        {
            while (true)
            {
                lock (lockObject)
                {
                    if (process == null)
                    {
                        return;
                    }

                    if (process.HasExited)
                    {
                        spawnWithoutLock();
                        log("find stopped process so restart");
                    }
                }
                await Task.Delay(checkIntervalMillseconds).ConfigureAwait(false);
            }
        }

        void spawnWithoutLock()
        {
            var info = new ProcessStartInfo(exePath);
            info.CreateNoWindow = true;
            info.UseShellExecute = false;
            info.RedirectStandardError = true;
            info.RedirectStandardInput = true;
            info.RedirectStandardOutput = true;
            info.CreateNoWindow = true;
            info.ErrorDialog = false;
            info.WindowStyle = ProcessWindowStyle.Hidden;

            process = Process.Start(info);
        }

        void stop()
        {
            lock (lockObject)
            {
                if (process != null)
                {
                    if (!process.HasExited)
                    {
                        process.Kill();
                    }
                    process.Close();
                    process = null;
                }
            }
        }

        void log(string format, params object[] args)
        {
            var message = string.Format(format, args);
            EventLog.WriteEntry(message);
        }
    }
}
```

# 実行ファイルをWindowsのServiceへ登録
sc.exeコマンドを使うとサービスへの登録、削除、スタートとストップがコマンドラインから実行できます。
「C:\Supervisor」にできたプログラムを設置します。

```
sc.exe create supervisor binPath= "C:\Supervisor\Suporvisor.exe" start= auto displayname= "Suporvisor"
sc.exe start supervisor
```

コントロールパネルからサービス一覧を見てみると先ほど登録したものが一覧に追加されます。
![services.png](https://qiita-image-store.s3.amazonaws.com/0/39587/c8b97fbe-c510-da27-1ca9-a807d478193d.png)

# イベントログの確認
コンソールプログラムと違って出力が見れないので、コントロールパネルからイベントビューアーを立ち上げてログを確認します。
うまく動いているようです。
![eventlog.png](https://qiita-image-store.s3.amazonaws.com/0/39587/bcfd0b09-6a30-b359-45ca-79acc0f98c1d.png)

# まとめ
Service登録すると以下のメリットがあります
- Windows Server再起動時に自動起動できる
- Serviceプログラム自体がクラッシュした時に自動再起動したりプログラムを実行したりとリアクションを登録できる
- 簡単にログが残せて標準のイベントビューアーで確認できる