MicrosoftVisualStudioInstallerProjects2022でインストーラーを作成した話

# 概要
今回は、Visual Studioで作成したアプリのインストーラーを作成するために
[Microsoft Visual Studio Installer Projects 2022]
を使用したので、設定した内容をまとめました。

# 参考
以下のリンク先を主に参考にしました。
.NETと.NET Frameworkでオフラインインストール時の必要ファイルが違う様です。
当記事では、.NET Framework版を記載しますので、.NETでの設定方法が必要な方はそちらも参考にしてみてください。

〇 .NET Frameworkでオフラインインストール設定

https://knkomko.hatenablog.com/entry/2020/05/17/032638

〇 .NETでの設定

https://zenn.dev/overdrive1708/articles/howto-vs2022-create-application-installer

〇 カスタム動作設定

https://www.softech.co.jp/mm_230201_tr.htm

https://qiita.com/okbear/items/8c006f498b77c7f1ecee

# 環境
Visual Studio 2022
.NET Framework 4.8.1

# Visual Studioの拡張機能のインストール
Visual Studioの[拡張機能]/[拡張機能の管理]から
『Microsoft Visual Studio Installer Projects 2022』
をインストールします。

# インストール対象のアプリ準備
インストール対象のアプリを作成します。
今回は『.NET Framework 4.8.1』で作成したアプリを対象としました。

# Setup Projectの設定

## プロジェクト追加
新しいプロジェクトの追加から『Setup Project』を追加します。

![SetupProject.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/d3d664a5-d405-459d-a405-80ec5e763623.png)

## プロパティ設定
『Setup Project』のプロパティウィンドウから各設定を更新します。

・Author : インストーラーの作成者
・InstallAllUsers : 全ユーザーを対象にインストールする場合は「True」にする
・Manufacturer : インストール対象のアプリケーションの製造者
・ProductName : インストール対象のアプリケーション名
・RemovePreviousVersions : 前のバージョンを削除する場合は｢True｣にする
・TargetPlatform : インストール対象のアプリケーションのPlatformに合わせる
・Title : インストーラーのタイトル
・Version : インストール対象のアプリケーションのバージョン

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/044dc9fc-0c22-4119-97be-30c22583f1f4.png)

## インストール対象のアプリ設定

『Setup Project』を右クリックし、[View] / [ファイルシステム]をクリックし、
『ApplicationFolder』を右クリックし、[Add] / [プロジェクト出力]をクリックします。
プロジェクト出力グループの追加ダイアログで、出力するプロジェクトを選択し、プライマリ出力を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/1252272d-ac5f-4a97-8ffa-9c3a29e5dea8.png)

設定が完了すると、『ApplicationFolder』に設定されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/f5c088f8-6a7e-43d7-9591-512651e80372.png)

また、この設定ではdllファイルとexeファイルのみ出力されるので、他に必要なファイルがある場合は、『ApplicationFolder』を右クリックし、[Add] / [ファイル]から追加してください。(例 : appSettings.json)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/b5bc31bb-4083-476e-ba8a-dd50e5493fbf.png)

## デスクトップショートカットの設定

『Setup Project』を右クリックし、[View] / [ファイルシステム]をクリックし、
『User's Desktop』をクリック後、右側の空欄で右クリックし、[新しいショートカットの作成]をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/95ef8176-af71-4bba-95a2-e1d2de760784.png)

『Look in』を『Application Folder』に変更し、「インストール対象の出力設定」を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/9cec9c1c-913f-44dc-9327-01fdc39cf063.png)

ショートカットが設定されるので、名称を変更してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/a2ef978d-90c1-45a3-ae25-d705041fb595.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/8c245f1a-a92b-4b10-94ba-514a5cd6cc86.png)

## スタートメニューの設定

デスクトップのショートカットの設定と同様に、『User's Programs Menu』で設定できます。

## 必須コンポーネント設定

アプリを実行するために必要となるコンポーネントを設定します。
Setup Projectのプロパティから「Prerequisites...」をクリックして、
必須コンポーネントの設定画面を開きます。

![SetupProjectProperty.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/76312bc3-ed7e-46c8-ae13-98cabb644731.png)

必須コンポーネントの設定画面で、使用するフレームワークに応じて必須コンポーネントを選択します。
今回は「4.8.1」を選択しました。

![Component.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/c9f12289-15c1-4ef2-886d-7b20de645719.png)

必須コンポーネントのインストール場所の設定は、
インストールさせたいPCがインターネットにつながっていない可能性があれば、
「アプリケーションと同じ場所から前提条件をダウンロードする」を選択します。
オフラインインストール設定をした場合のビルド時にファイル不足のエラーが出ることがあります。
後述のトラブルシューティング&QAを確認してください。

必須コンポーネントを設定後のビルドで、下記のエラーが出る場合は、
インストールアプリを設定した際に作成される依存パッケージの『Microsoft.NetFramework』のVersionを使用するフレームワークに更新します。

```
WARNING: The version of the .NET Framework launch condition '.NET Framework 4.7.2' does not match the selected .NET Framework bootstrapper package. Update the .NET Framework launch condition to match the version of the .NET Framework selected in the Prerequisites Dialog Box.
WARNING: The target version of the .NET Framework in the project does not match the .NET Framework launch condition version '.NET Framework 4.7.2'. Update the version of the .NET Framework launch condition to match the target version of the.NET Framework in the Advanced Compile Options Dialog Box (VB) or the Application Page (C#, F#).
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/a1405fe7-7756-411c-b4cf-1b783fdeabfb.png)

## ユーザーインターフェイスとインストール画面
ユーザーインターフェイス設定では、インストール前の確認ダイアログを設定することができます。

『Setup Project』を右クリックし、[View] / [ユーザーインターフェイス]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/747437ef-3801-4a95-9965-10f7f9205b21.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/232dcd6a-3289-4fa6-b562-84f3fcda2efc.png)

『Setup Project』を右クリックし、インストールをクリックすると、実際にインストーラーを使用してインストールできるので、それぞれの画面を確認してください。
[Install] / [Start]の中のダイアログに関する設定を変更することがあったので、それぞれ変更した箇所を記載します。

### [Start] / [ようこそ]
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/e4400633-013c-424f-bfd3-56e2d4078cb0.png)

「ProductName」の箇所を変更したい場合、『Setup Project』のプロパティから「ProductName」を更新してください。


### [Start] / [インストールフォルダー]
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/f15e7db3-edb7-473b-a478-ee1b4963c706.png)

この画面では、「インストール先のフォルダー」と「インストール対象のユーザー」を設定できます。

デフォルトで設定されるインストール先のフォルダーのパスを変更したい場合は、『ファイルシステム』Viewの『ApplicationFolder』のプロパティの「DefaultLocation」を更新してください。デフォルトで設定されている様に自動で変更される様にすることも、固定の絶対パスを指定することもできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/171eb7eb-ec83-4323-bc9a-f0b1fc7edf86.png)

インストール対象のユーザーの選択肢を非表示にしたい場合は、『ユーザーインターフェイス』Viewの『インストールフォルダー』のプロパティの「InstallAllUsersVisible」を変更してください。
デフォルトで選択されている項目を変更したい場合は、『Setup Project』のプロパティの「InstallAllUsers」を変更してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/601f9809-2fef-4d4a-ae38-294ef20957d7.png)

## ユーザーインターフェイスとカスタム動作設定
ユーザーインターフェイス設定では、汎用的な確認ダイアログを追加設定することができ、その回答に応じて、カスタム動作を実行することができます。

### 確認ダイアログの追加
『ユーザーインターフェイス』Viewの『Start』で右クリックし、[ダイアログの追加]をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/a5c97d34-118d-4e0c-9eb1-44b0f3f702bb.png)

追加したダイアログのプロパティの確認内容や選択肢、カスタム動作に渡すためのKeyとなる名称を更新します。
ダイアログの種類によって、Keyとなるプロパティは異なります。

(例)
オプションボタン(ラジオボタン) : ButtonProperty
チェックボックス : CheckboxProperty

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/614c4972-be9d-4c0d-b0a5-8af9d3569d41.png)

### カスタム動作プロジェクトの追加
新しいプロジェクトの追加から『C# クラスライブラリ』を追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/5742416b-2fd2-48cc-a51d-123c6badc84a.png)

「System.Configuration.Install」と
Debug用に「System.Windows.Forms」の参照を追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/40c89e45-4464-4451-bca1-0d166d1d820e.png)

Installerクラスの派生クラスを作成し、Run Installer Attribute属性をtrueで追加します。

```
using System.Collections;
using System.Windows.Forms;

namespace CustomInstallAction
{
    [System.ComponentModel.RunInstaller(true)]
    public class CustomInstallAction : System.Configuration.Install.Installer
    {
        /// <inheritdoc/>
        public override void Install(IDictionary stateSaver)
        {
            base.Install(stateSaver);
            MessageBox.Show("Install");
        }

        /// <inheritdoc/>
        public override void Commit(IDictionary savedState)
        {
            base.Commit(savedState);
            MessageBox.Show("Commit");
        }

        /// <inheritdoc/>
        public override void Rollback(IDictionary savedState)
        {
            base.Rollback(savedState);
            MessageBox.Show("Rollback");
        }

        /// <inheritdoc/>
        public override void Uninstall(IDictionary savedState)
        {
            base.Uninstall(savedState);
            MessageBox.Show("Uninstall");
        }
    }
}
```

### インストーラーにカスタム動作の追加
『Setup Project』を右クリックし、[View] / [カスタム動作]をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/42e43063-3967-4246-becc-a5dd4e2888e9.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/904f6050-86bb-440c-9362-d159a4253f86.png)

Install・Commit・Rollback・Uninstallから追加したいタイミングを右クリックし、カスタム動作の追加を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/80e0e478-a65e-4778-95ca-f1faee6f090a.png)

『Application Folder』をクリックし、「Add Output...」からカスタム動作プロジェクトを追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/0b8a5fc8-380a-4bc8-a2de-7f796e990e53.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/1648b9ab-071c-4c2c-a741-46c728293525.png)

追加したプロジェクトを選択し、「OK」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/f1842717-0d61-45f1-b4f7-a489b6029c84.png)

### カスタム動作へのデータの渡し方
ユーザーの選択した内容をカスタム動作に渡す設定を行います。

追加したカスタム動作のプロパティの「CustomActionData」に下記のルールで記載します。
・カスタム動作に渡すためのKeyとなる名称やWindowsInstallerのプロパティは[]で囲む
・値にスペースが含まれる時は""で囲む
・データが複数ある場合は、半角スペースで区切る

例
/ButtonResult1=[BUTTON1] /TextResult2="[TEXT2]"

カスタム動作側では、下記の様にデータを取得することができます。
この際、「AssemblyPath」の様な「CustinActionData」に設定していないが、デフォルトで設定されているデータも取得することができます。

```
        /// <inheritdoc/>
        public override void Install(IDictionary stateSaver)
        {
            base.Install(stateSaver);
            
            string buttonResult = this.Context.Parameters["ButtonResult1"];
            string textResult = this.Context.Parameters["TextResult2"];
            MessageBox.Show($"{buttonResult}:{textResult}");

            // インストール先ディレクトリを取得します。
            var destDirPath = System.IO.Path.GetDirectoryName(this.Context.Parameters["AssemblyPath"]);
            MessageBox.Show(destDirPath);

            MessageBox.Show("Install");
        }
```

取得したデータを用いて、実行したい処理を記載してください。

# トラブルシューティング & QA

## 必須コンポーネントをオフラインインストールする設定の際に、ファイル不足のエラーが出る

必須コンポーネント設定で「アプリケーションと同じ場所から前提条件をダウンロードする」を選択後のビルドで、以下のエラーが出る場合、ファイルが不足しています。

```
ERROR: [必須コンポーネント] ダイアログ ボックスで [アプリケーションと同じ場所から必須コンポーネントをダウンロードする] を有効にするには、項目 'Microsoft .NET Framework 4.8.1 (x86 および x64)' のファイル 'DotNetFX481\NDP481-x86-x64-AllOS-ENU.exe' をローカル コンピューターにダウンロードする必要があります。詳細については、http://go.microsoft.com/fwlink/?LinkId=616018 を参照してください。
ERROR: [必須コンポーネント] ダイアログ ボックスで [アプリケーションと同じ場所から必須コンポーネントをダウンロードする] を有効にするには、項目 'Microsoft .NET Framework 4.8.1 (x86 および x64)' のファイル 'DotNetFX481\NDP481-x86-x64-AllOS-JPN.exe' をローカル コンピューターにダウンロードする必要があります。詳細については、http://go.microsoft.com/fwlink/?LinkId=616018 を参照してください。
```

### 不足ファイルのダウンロード
以下のサイトから使用している.NET Frameworkのオフラインインストーラー と 言語パックのファイルをダウンロードします。
・オフラインインストーラー : NDP481-x86-x64-AllOS-ENU.exe
・言語パック : ndp481-x86-x64-allos-jpn.exe

https://dotnet.microsoft.com/ja-jp/download/dotnet-framework

![OfflineInstaller.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/4efd6918-5e62-4ac2-800a-c0acc5a443a7.png)

### ダウンロードしたファイルを配置
ダウンロードしたファイルを規定のフォルダに配置します。
※ .NETと.NET Frameworkでは規定のフォルダが異なる様です。

以下のフォルダを開き、オフラインインストーラーを配置します。
C:\Program Files (x86)\Microsoft SDKs\ClickOnce Bootstrapper\Packages\DotNetFX481
※ .Net Frameworkのバージョンにより「DotNetFX～～」の部分が変わります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/a356e3ce-a660-456d-b1ad-52dbabfb100d.png)

「DotNetFX481」フォルダ内の「ja」フォルダを開き、言語パックを配置します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/85fdc3c0-a094-4718-bf63-295b46087233.png)

再度ビルドした際にエラーが出ないことを確認してください。

## ユーザーインターフェイスViewで波線がついたままになる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/e3a8f347-81d5-4b4c-8bd7-3a6b50686763.png)

この状態でもビルドは成功しますが、警告が出ます。

```
WARNING: All custom dialogs must precede the 'Installation Folder' dialog.
```

カスタムダイアログは『インストールフォルダー』より前に配置する必要があるとのことなので、その様にダイアログの位置を調整してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/91dbc3d2-616e-4f2c-95ae-428edcded697.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853694/35ff48a1-d514-45c6-862c-249f8df734c6.png)

再度ビルドした際に警告が出ないことを確認してください。

### ユーザーインターフェイスの確認ダイアログを調整したい
汎用のダイアログは多くないので、レイアウトを調整したい場合、カスタム動作中に自作のダイアログを表示するという方法があります。
また、確認ダイアログを表示するかどうかを調整したい場合（例 初回インストール時のみ表示したい）等でも、カスタム動作で表示の必要性を確認した後に表示する処理を実行する必要があるかと思われます。
（イマイチ表示するかどうかの調整ほうほがわかりませんでした。）