WiXSharpでサービスを登録する方法と、WiXSharpで日本語を含めたときにランタイムエラーになるのを防ぐ方法

# はじめに

## 注意

今回の記事は[前回](https://qiita.com/nao-a/items/b021ca548fab266e7e32)の記事の続きになります。
用意しているコードはすべて[前回](https://qiita.com/nao-a/items/b021ca548fab266e7e32)の続きからとなりますのでご注意ください。

## あらためて、はじめに

Windows向けのインストーラを作成する際に使用されるプロダクトはいくつかあると思います。
その中でWiXSharpはコードですべてを完結させることができるインストーラ作成のプロダクトとなります。
ベースはWiXとなっておりますので、カスタマイズ性は非常に高く、VisualStudio Installer Projectでは対応していない、
インストール時の **「.NET5で作成されたWindowsServiceアプリのサービスへの登録」に対応** しています。

この記事では、他の方が解説されているWiXSharpそのものの使い方ではなく、
.NET5で作成されたWindowsServiceの扱い方、および、日本語を含めた際に発生するランタイムエラーの対応方法について記載致します。
よろしければご覧ください。

また、この記事で紹介しているコードは以下のリポジトリに配置しております。

https://github.com/cocoalix/WindowsServiceTestWithDotNet5

## 実行環境

- VisualStudio2019
- Windows10Pro 20H2 (Build: 19042.867)
- .NET5 SDK 5.0.1

# 参考

https://qiita.com/skitoy4321/items/111ca55899473b9c2df4

https://github.com/oleg-shilo/wixsharp

https://github.com/oleg-shilo/wixsharp/issues/407

# 実装

以下のようなインストーラの実装を予め用意しておきます。
なお、以下の実装で、掲題の「WiXSharpでサービスを登録する方法」及び「WiXSharpで日本語を含めたときにランタイムエラーになる」問題は解決されています。

```csharp
using System;
using System.Xml.Linq;

namespace Installer
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                WixSharp.File ui = new WixSharp.File(
                    @"cli\WindowsServiceTestClient.exe",
                    new WixSharp.FileShortcut("WindowsServiceTestClient", @"%ProgramMenu%\cocoalix") { Advertise = true }
                );
                WixSharp.File service = new WixSharp.File(
                    @"service\WindowsServiceTest.exe"
                );
                WixSharp.Files uiDepends = new WixSharp.Files(
                    @"cli\*.*"
                );
                WixSharp.Files serviceDepends = new WixSharp.Files(
                    @"service\*.*"
                );

                var dir = new WixSharp.Dir(new WixSharp.Id("InstallDir"),
                    @"%ProgramFiles%\cocoalix",
                    // インストーラーにインクルードするファイル
                    ui,
                    // インストーラーにインクルードするファイル
                    service,
                    uiDepends,
                    serviceDepends
                );

                var project = new WixSharp.ManagedProject("ウィンドウズサービステスト", dir);

                // 日本語をインストーラ及びWiXで仕様するためのおまじない
                project.Codepage = "932";
                project.Language = "ja-JP";

                // アップグレードコードを指定 (このコードをコピペした場合は必ず変更すること / 運用開始後は変更厳禁)
                project.GUID = new Guid("abbb7cf9-19fa-45f2-babc-a35312741772");

                // インストーラーのファイル名
                project.OutFileName = "ウィンドウズサービステストインストーラ";

                service.ServiceInstaller = new WixSharp.ServiceInstaller
                {
                    Account = @"NT Authority\System",
                    PermissionEx = new WixSharp.PermissionEx()
                    {
                        User = @"Administrator",
                        ServicePauseContinue = false,
                        ServiceStart = true,
                        ServiceStop = true,
                    },
                    Name = "WindowsServiceTestService",
                    Description = "ウィンドウズサービステストのサービスです",
                    StartOn = WixSharp.SvcEvent.Install,
                    StopOn = WixSharp.SvcEvent.InstallUninstall_Wait,
                    RemoveOn = WixSharp.SvcEvent.Uninstall_Wait,
                    DelayedAutoStart = true,
                    FirstFailureActionType = WixSharp.FailureActionType.restart,
                    SecondFailureActionType = WixSharp.FailureActionType.restart,
                    ThirdFailureActionType = WixSharp.FailureActionType.none,
                    PreShutdownDelay = 1000 * 60 * 3,
                    ServiceSid = WixSharp.ServiceSid.none,
                };

                // インストーラで表示するウィンドウ群の指定
                project.ManagedUI = new WixSharp.ManagedUI();
                project.ManagedUI.InstallDialogs.Add(WixSharp.Forms.Dialogs.Welcome)
                                                .Add(WixSharp.Forms.Dialogs.Licence)
                                                .Add(WixSharp.Forms.Dialogs.Progress)
                                                .Add(WixSharp.Forms.Dialogs.Exit);

                project.LicenceFile = @"Eula.rtf";

                project.PreserveTempFiles = true;
                project.WixSourceGenerated += _WixSourceGenerated;

                project.BuildMsi();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }

        private static void _WixSourceGenerated(XDocument document)
        {
        }

    }
}
```

この時、以下に注目して欲しいのですが、なんとここで記載されている `cli\` や `service\` は自分で用意しなくてはなりません。
相対パスでビルドできるかと思って試しましたが、全く上手くいきませんでした。
つまり、自分でプロジェクトと同じ位置にインストール対象のプロジェクトのビルド結果を自分で配置してやらないとならないのです。

```csharp
                WixSharp.File ui = new WixSharp.File(
                    @"cli\WindowsServiceTestClient.exe",
                    new WixSharp.FileShortcut("WindowsServiceTestClient", @"%ProgramMenu%\cocoalix") { Advertise = true }
                );
                WixSharp.File service = new WixSharp.File(
                    @"service\WindowsServiceTest.exe"
                );
                WixSharp.Files uiDepends = new WixSharp.Files(
                    @"cli\*.*"
                );
                WixSharp.Files serviceDepends = new WixSharp.Files(
                    @"service\*.*"
                );
```

ので、プロジェクトのビルド前/ビルド後に以下のコマンドを実行してやります。

```ビルド前イベントのコマンドライン
xcopy $(SolutionDir)WindowsServiceTestClient\bin\Debug\net5.0 $(ProjectDir)cli\ /q /e /y
xcopy $(SolutionDir)WindowsServiceTest\bin\Debug\net5.0 $(ProjectDir)service\ /q /e /y
```

```ビルド後イベントのコマンドライン
xcopy $(ProjectDir)cli $(TargetDir)cli\ /q /e /y
xcopy $(ProjectDir)service $(TargetDir)service\ /q /e /y

rmdir /s /q $(ProjectDir)cli
rmdir /s /q $(ProjectDir)service
```

これでこの問題は解決できます。

さて、掲題の件に移ります。

## WiXSharpでサービスを登録する方法

以下の部分がサービスの登録を行ってくれています。

```csharp
service.ServiceInstaller = new WixSharp.ServiceInstaller
                {
                    Account = @"NT Authority\System",
                    PermissionEx = new WixSharp.PermissionEx()
                    {
                        User = @"Administrator",
                        ServicePauseContinue = false,
                        ServiceStart = true,
                        ServiceStop = true,
                    },
                    Name = "WindowsServiceTestService",
                    Description = "ウィンドウズサービステストのサービスです",
                    StartOn = WixSharp.SvcEvent.Install,
                    StopOn = WixSharp.SvcEvent.InstallUninstall_Wait,
                    RemoveOn = WixSharp.SvcEvent.Uninstall_Wait,
                    DelayedAutoStart = true,
                    FirstFailureActionType = WixSharp.FailureActionType.restart,
                    SecondFailureActionType = WixSharp.FailureActionType.restart,
                    ThirdFailureActionType = WixSharp.FailureActionType.none,
                    PreShutdownDelay = 1000 * 60 * 3,
                    ServiceSid = WixSharp.ServiceSid.none,
                };
```

## WixSharpで日本語を含めたときにランタイムエラーになるのを防ぐ方法

これは次のコードで解決できています。

```csharp
                project.Codepage = "932";
                project.Language = "ja-JP";
```

## 結果として

このようにインストーラが作成できますし、サービスに登録されます。

![ウィンドウズサービステスト Setup 2021_03_28 22_37_48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507143/2fdf5da9-409d-07dd-385c-c4656eae07d2.png)
![ウィンドウズサービステスト Setup 2021_03_28 22_37_51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507143/f171e584-9c9f-1ff2-dcc3-3d5f213c7de6.png)
![サービス 2021_03_28 22_46_27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507143/b11e2c93-63c4-500f-2609-38c001e86ecf.png)

# おわりに

このような形で、WiXSharpを使用して日本語が使用されたインストーラは作成できますし、サービスへ登録することができます。
同じ開発者の助けになる記事になることを望みます。

また、何か誤っている個所がありましたらご指摘ください。
ここまで読んでくださり、ありがとうございました。

[次回](https://qiita.com/nao-a/items/ac91968d31fabcecd2b3)