WixUI の標準ダイアログの動作をカスタマイズする

前回投稿した下記記事の「さらなる問題」において、プロパティ```ARPNOREMOVEPP```を設定することにより「アプリと機能」や「プログラムと機能」で「アンインストール」を無効にした場合、WixUIの標準ダイアログである「メンテナンス種別選択画面」（正式名称ではないが便宜上そう呼ぶことにする）でも「削除」ボタンが使用不可となる問題が発生していた。

https://qiita.com/osamu-ikawa/items/8a0fe0a5554a60fa1c85

結論から言うと、この問題は「メンテナンス種別選択画面」でも、```ARPNOREMOVEPP```の値により「削除」ボタンの使用可否の切り替えが行われていることに起因している。

この記事では問題解決のために、「メンテナンス種別選択画面」の動作をカスタマイズする方法について説明する。

## WiX Toolset のソースコードをダウンロードする

「メンテナンス種別選択画面」の仕様を把握するために、まず Github より WiX Toolset のソースコード一式をダウンロードする。

https://github.com/wixtoolset/wix3/releases/tag/wix3112rtm

現時点(2022/07/04)　の最新版である、Ver.3.11.2 のソースコードは上記URLからダウンロードできる。「Assets」のうち、「Source code (zip)」でも「Source code (tar.gz)」でも、好きなアーカイブ形式を選んでダンロードすればよい。

アーカイブを展開したディレクトリツリーのうち、「src\ext\UIExtension\wixlib」には、WixUIの標準ダイアログの内容や画面遷移が定義された *.wxs ファイル、およびダイアログで使用されるメッセージが言語別に定義された *.wxl ファイルが多数格納されている。

## 「メンテナンス種別選択画面」のソースコード

上述のディレクトリに格納された、 *.wxs ファイルのうち、 MaintenanceTypeDlg.wxs に「メンテナンス種別選択画面」が定義されているので、その内容を確認してみる。

下記のコードは、 MaintenanceTypeDlg.wxs から「削除」ボタン(```RemoveButton```)およびその説明テキスト(```RemoveText```、```RemoveDisabledText```)に該当する部分を抜粋したものである。
```xml:MaintenanceTypeDlg.wxs
<Control Id="RemoveButton" Type="PushButton" X="40" Y="171" Width="80" Height="17" ToolTip="!(loc.MaintenanceTypeDlgRemoveButtonTooltip)" Text="!(loc.MaintenanceTypeDlgRemoveButton)">
    <Publish Property="WixUI_InstallMode" Value="Remove">1</Publish>
    <Condition Action="disable">ARPNOREMOVE</Condition>
</Control>
<Control Id="RemoveText" Type="Text" X="60" Y="191" Width="280" Height="20" NoPrefix="yes" Text="!(loc.MaintenanceTypeDlgRemoveText)">
    <Condition Action="hide">ARPNOREMOVE</Condition>
</Control>
<Control Id="RemoveDisabledText" Type="Text" X="60" Y="191" Width="280" Height="20" NoPrefix="yes" Text="!(loc.MaintenanceTypeDlgRemoveDisabledText)" Hidden="yes">
    <Condition Action="show">ARPNOREMOVE</Condition>
</Control>
```
「削除」ボタンやその説明テキストは```<Condition Action="disable">ARPNOREMOVE</Condition>```のように```ARPNOREMOVE```プロパティによって、使用可否や表示/非表示が制御されており、このプロパティの値が設定されていない状態にすれば、「削除」ボタンを使用可能にできることが分かる。

## 「削除」ボタンを使用可能にする

特定のタイミングおよび条件で```ARPNOREMOVE```プロパティを変更するには、下記のようなカスタムアクションを実行すればよい。
```xml
<CustomAction 
	Id="TurnNoRemoveOff"
	Property="ARPNOREMOVE"
	Value=""
	/>
```
このカスタムアクションを「メンテナンス種別選択画面」が表示される時に実行すればよいわけだが、具体的にどのように設定すれば実現可能なのか？ それを探るために、ビルドされたMSIファイルを[orca](https://docs.microsoft.com/ja-jp/windows/win32/msi/orca-exe)で覗いてみる。
![orca_install_ui_sequence.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726086/77ad01eb-c93b-b943-e79f-fae5ca74c0ca.png)
このスクリーンショットでは、oraca で```InstallUISequence```テーブルの内容が表示されている。

複数定義された```Action```のうち```MaintenanceWelcomDlg```は「メンテナンス種別選択画面」の前に表示されるウェルカム画面であり、この画面が表示される時に同じ条件で、上述のカスタムアクション```TurnNoRemoveOff```を実行すれば、その次に表示される「メンテナンス種別選択画面」でも```ARPNOREMOVE```プロパティに値が設定されていない状態にすることができる。

```MaintenanceWelcomDlg```が表示される条件は```Condition```列を見ると```Installed AND NOT RESUME AND NOT Preselected AND NOT PATCH```となっているので、これらの情報を踏まえて```InstallUISequence```を下記のように設定する。
```xml
<InstallUISequence>
	<Custom Action="TurnNoRemoveOff" Before="MaintenanceWelcomeDlg">
		Installed AND NOT RESUME AND NOT Preselected AND NOT PATCH
	</Custom>
</InstallUISequence>
```

## カスタマイズ結果

前節のとおり、カスタムアクションにより```ARPNOREMOVEPP```プロパティの値を変更するようにしたインストーラを用いてアプリをインストールし、「アプリと機能」より「変更」を選択すると、「メンテナンス種別選択画面」は下記のようになる。
![maintenance_type_customised.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726086/810b8542-f972-e5f9-ecb4-8346ce1a25c2.png)
「削除」ボタンを使用可能とすることができた。