Windows Installer手引書 Part.21 WiX ToolsetのUIを改造する１

[**前の記事へ**](https://qiita.com/tohshima/items/d08ab298e57fd56e7bde)　　[**目次へ**](https://qiita.com/tohshima/items/dc4c4721d50a2ad70c7a)　　[**次の記事へ**](https://qiita.com/tohshima/items/9758f947e9567ad6ffa9)

## ダイアログボックスのデザインとMSIファイルの関係

Windows Installerでは、ユーザーにウィザード形式でダイアログボックスを表示して、インストールに関する情報を表示・設定します。このダイアログボックスのデザインは、古典的なWindows標準のUI部品が使用されます[^1]が、ウィンドウ上の文章やボタンの配置などUI部品を自由に設計可能で、その設計情報はMSIファイル内にいくつかのテーブルに分散して格納されています。この格納方法が決して分かりやすいものではないため、ダイアログのデザインはWiX Toolsetなどのツール側に用意されたものを、必要に応じて改造しながら使用するのが賢いやり方[^2]です。ただ、UI部品の情報がどのようにMSIファイルのテーブルに格納されているか知ることで、改造が容易になり、凝ったUIの表示も可能になります。

今回はこのような事情もあって、Windows Installerが扱えるUIの説明の入り口として、WiX ToolsetでUIをカスタマイズする方法を説明します。

## WiX ToolsetのUIセット

WiX Toolsetには、いくつかのダイアログボックスの画面デザインと、これらの表示順を規定したDialog Setが用意されています。これらのダイアログは、WiX Toolsetのソースに含まれるファイルを改造することで自由にデザインを変更できます。ダイアログ関連のソースファイルは、WiX Toolsetのダウンロードページ（WiX Toolset公式ページの[Downloads](https://wixtoolset.org/releases/)にダウンロードページのリンクがあります。）にある`Source code`(wix3-wixXXXrtm.zip または wix3-wixXXXrtm.tar.gz。XXXは数字、執筆時点のファイル名は`wix3-wix3112rtm.zip`および`wix3-wix3112rtm.tar.gz`でした）を解凍すると、`src\ext\UIExtension\wixlib`フォルダ以下に格納されています。ここにあるファイルを簡単に分類すると下表のようになっています。

|Dialog Set|Dialogs|Localization File|
|:--|:--|:--|
|WixUI_Advanced.wxs<br>WixUI_FeatureTree.wxs<br>WixUI_InstallDir.wxs<br>WixUI_Minimal.wxs<br>WixUI_Mondo.wxs|AdvancedWelcomeEulaDlg.wxs<br>BrowseDlg.wxs<br>CancelDlg.wxs<br>CustomizeDlg.wxs<br>DiskCostDlg.wxs<br>ErrorDlg.wxs<br>ErrorProgressText.wxs<br>ExitDialog.wxs<br>FatalError.wxs<br>FeaturesDlg.wxs<br>FilesInUse.wxs<br>InstallDirDlg.wxs<br>InstallScopeDlg.wxs<br>InvalidDirDlg.wxs<br>LicenseAgreementDlg.wxs<br>MaintenanceTypeDlg.wxs<br>MaintenanceWelcomeDlg.wxs<br>MsiRMFilesInUse.wxs<br>OutOfDiskDlg.wxs<br>OutOfRbDiskDlg.wxs<br>PrepareDlg.wxs<br>ProgressDlg.wxs<br>ResumeDlg.wxs<br>SetupTypeDlg.wxs<br>UserExit.wxs<br>VerifyReadyDlg.wxs<br>WaitForCostingDlg.wxs<br>WelcomeDlg.wxs<br>WelcomeEulaDlg.wxs|WixUI_ar-SA.wxl<br>WixUI_bg-BG.wxl<br>WixUI_ca-ES.wxl<br>WixUI_cs-CZ.wxl<br>WixUI_da-DK.wxl<br>WixUI_de-de.wxl<br>WixUI_el-GR.wxl<br>WixUI_en-us.wxl<br>WixUI_es-es.wxl<br>WixUI_et-EE.wxl<br>WixUI_fi-FI.wxl<br>WixUI_fr-fr.wxl<br>WixUI_he-IL.wxl<br>WixUI_hi-IN.wxl<br>WixUI_hr-HR.wxl<br>WixUI_hu-HU.wxl<br>WixUI_it-it.wxl<br>WixUI_ja-jp.wxl<br>WixUI_kk-KZ.wxl<br>WixUI_ko-KR.wxl<br>WixUI_lt-LT.wxl<br>WixUI_lv-LV.wxl<br>WixUI_nb-NO.wxl<br>WixUI_nl-NL.wxl<br>WixUI_pl-pl.wxl<br>WixUI_pt-BR.wxl<br>WixUI_pt-PT.wxl<br>WixUI_ro-RO.wxl<br>WixUI_ru-ru.wxl<br>WixUI_sk-SK.wxl<br>WixUI_sl-SI.wxl<br>WixUI_sq-AL.wxl<br>WixUI_sr-Latn-CS.wxl<br>WixUI_sv-SE.wxl<br>WixUI_th-TH.wxl<br>WixUI_tr-TR.wxl<br>WixUI_uk-UA.wxl<br>WixUI_zh-CN.wxl<br>WixUI_zh-HK.wxl<br>WixUI_zh-TW.wxl|

`Dialog Set`の各ファイルは`Dialogs`の各画面を表示する順番を規定しており、`Localization File`は各ダイアログボックスに表示されるローカライズされた文面が格納されています。これらのファイルはコンパイルされ、`C:\Program Files (x86)\WiX Toolset v3.11\bin\WixUIExtension.dll`として提供されています（執筆時点のパス）。改造が不要な場合はこれをそのまま使えば良いわけですが、例えばどれかのダイアログボックスをスキップさせたい場合は`Dialog Set`のファイルを自分のプロジェクトにコピーしてきて改造すれば可能です。また、画面デザインに手を入れたければ、`Dialogs`のファイルを自分のプロジェクトにコピーしてきて改造し、`Dialog Set`側でそのダイアログボックスを表示するように変更すれば可能です。文面を変えたければ、`Localization File`のファイルに書式を合わせて、拡張子`.wxl`のファイルを作成すれば良いのです。

## ダイアログボックスをスキップする

`WixUI_FeatureTree`Dialog Setのソース`WixUI_FeatureTree.wxs`を以下に示します（説明に不要なコメント文は削除してあります）。
![WixUI_FeatureTree.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83170/4274bdd7-81ba-be8c-1ccc-97c6ef1447de.png)

このファイルを自分のプロジェクトのフォルダにコピーして、適当なファイル名に変更します。Visual Studioで作業しているなら、プロジェクトに追加してやります。図中、(1)の部分で使用するダイアログボックスを定義し、(2)の部分で主に[次へ]ボタンや[戻る]ボタンを押した後に表示するダイアログボックスを定義しています。(2)の部分を読むと、新規インストール時（NOT Installedの時）に`Next`ボタンの動作をたどっていくと、次のようなルートをたどるのがわかります。
　`WelcomeDlg`→`LicenseAgreementDlg`→`CustomizeDlg`→`VerifyReadyDlg`
ここで、`LicenseAgreementDlg`をスキップしてみましょう。`WelcomeDlg`で`Next`を押したとき`CustomizeDlg`に移動すればよいので、(4)の部分を`CustomizeDlg`に書き換えます。画面をスキップするための変更はこれだけですが、このファイルを改造して使う際には、`WixUIExtension.dll`に入っているダイアログボックスのデザインを使用する必要があります。`Id=WixUI_FeatureTree`のDialog Setもこの中に入っているため、改造用のファイルで同じIdを使えません。そこで、(3)のIdを変更してから改造していきます。変更後のファイルは下記のようになります。

```XML:WixUI_Part21.wxs
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Fragment>
        <UI Id="WixUI_Part21">
            <TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" />
            <TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="12" />
            <TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="9" Bold="yes" />

            <Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
            <Property Id="WixUI_Mode" Value="FeatureTree" />

            <DialogRef Id="ErrorDlg" />
            <DialogRef Id="FatalError" />
            <DialogRef Id="FilesInUse" />
            <DialogRef Id="MsiRMFilesInUse" />
            <DialogRef Id="PrepareDlg" />
            <DialogRef Id="ProgressDlg" />
            <DialogRef Id="ResumeDlg" />
            <DialogRef Id="UserExit" />

            <Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999">1</Publish>

            <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="CustomizeDlg">NOT Installed</Publish>
            <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">Installed AND PATCH</Publish>

            <Publish Dialog="LicenseAgreementDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg">1</Publish>
            <Publish Dialog="LicenseAgreementDlg" Control="Next" Event="NewDialog" Value="CustomizeDlg">LicenseAccepted = "1"</Publish>

            <Publish Dialog="CustomizeDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="1">Installed</Publish>
            <Publish Dialog="CustomizeDlg" Control="Back" Event="NewDialog" Value="LicenseAgreementDlg" Order="2">NOT Installed</Publish>
            <Publish Dialog="CustomizeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>

            <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="CustomizeDlg" Order="1">NOT Installed OR WixUI_InstallMode = "Change"</Publish>
            <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="2">Installed AND NOT PATCH</Publish>
            <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="3">Installed AND PATCH</Publish>

            <Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog" Value="MaintenanceTypeDlg">1</Publish>

            <Publish Dialog="MaintenanceTypeDlg" Control="ChangeButton" Event="NewDialog" Value="CustomizeDlg">1</Publish>
            <Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
            <Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
            <Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog" Value="MaintenanceWelcomeDlg">1</Publish>
        </UI>

        <UIRef Id="WixUI_Common" />
    </Fragment>
</Wix>
```

このDialog Setを使うよう、`Product.wxs`も変更します。UIRefエレメントで上記のUIエレメントに指定したIdを指定してやるだけです。

```XML:Product.wxs
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
	<Product Id="59FEE1D2-A612-40B0-96B5-FDE465304B9B" Name="Part21_01" Language="1033" Version="1.0.0" Manufacturer="tohshima" UpgradeCode="05fd1b28-50a8-4d86-92fa-5adfe2fae0de">
		<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />

		<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
		<MediaTemplate />

        <UIRef Id="WixUI_Part21"/>

		<Feature Id="ProductFeature" Title="Part21_01" Level="1">
			<ComponentGroupRef Id="ProductComponents" />
		</Feature>
	</Product>

	<Fragment>
		<Directory Id="TARGETDIR" Name="SourceDir">
			<Directory Id="ProgramFilesFolder">
				<Directory Id="INSTALLFOLDER" Name="Part21_01" />
			</Directory>
		</Directory>
	</Fragment>

	<Fragment>
		<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
            <Component Id="cExe" Guid="{D5B5ADA3-AC19-4B5D-A5CB-7FBBB2548530}" >
                <File Id="fExe" Source="C:\Windows\System32\calc.exe" KeyPath="yes" />
            </Component>
		</ComponentGroup>
	</Fragment>
</Wix>
```

これで、新規インストール時に`LicenseAgreementDlg`がスキップされます。


[^1]: Windows Installerには外部のプログラムにUI表示を任せる仕組みもあります。これを使って、インストールの進捗状況を外部のプログラムに表示されることもできます。
[^2]: Windows SDKでインストーラーを作る場合も、SDKが用意したテンプレートを改造していくことになります。MSI形式のインストーラーがどれも似たようなUIを持つのも、インストーラー作成ツールがこのテンプレートを手本にしているからでしょう。`Microsoft Windows SDK for Windows 7 and .NET Framework 4`を使っているなら、`C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\msitools`フォルダ以下にいくつかMSIが置いてあるので、興味があれば見てみてください。

[**前の記事へ**](https://qiita.com/tohshima/items/d08ab298e57fd56e7bde)　　[**目次へ**](https://qiita.com/tohshima/items/dc4c4721d50a2ad70c7a)　　[**次の記事へ**](https://qiita.com/tohshima/items/9758f947e9567ad6ffa9)