【WiX Toolset】「アプリと機能」で「アンインストール」ボタンだけを無効にする

通常WiX Toolset(以降単にWiXとする)で作成したインストーラで、アプリをインストールすると、Windowsの「アプリと機能」では次のように「変更」と「アンインストール」が表示される。
![apps_normal.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726086/71e9805a-8b04-ffbf-8e2e-75a7f1bbe7c0.png)
これに対し、旧来のコントロールパネルより利用できる「プログラムと機能」では、アプリを右クリックすると、次のように「アンインストール」、「変更」、「修復」を選択可能なコンテキストメニューが表示される。
![programs_normal.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726086/7a904113-fc24-c8cd-657a-400341732fa0.png)
この記事では、これらの選択肢のうち「アンインストール」のみを非表示または使用不可にする方法について説明する。

## 「アンインストール」を非表示/使用不可にする

Windows Installerのプロパティ **ARPNOREMOVE** に **1** を設定する。
```xml
<Property Id="ARPNOREMOVE">1</Property>
```
すると、「プログラムと機能」のコンテキストメニューでは、「アンインストール」が非表示となる。
![programs_noremove.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726086/6230e696-3ec8-e405-549c-4ec1a8705acd.png)
しかし「アプリと機能」では、「アンインストール」と同時に「変更」までもが使用不可となってしまう。
![apps_noremove.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726086/5b2f8a56-58db-5e7e-6344-cfb498877f9f.png)

## 「変更」を使用可能にする

「アプリと機能」や「プログラムと機能」の表示には、下記のレジストリキーの内容が反映される。（```{ProductCode}``` は製品毎に付与されたGUID）
```
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{ProductCode}
```
そこで、 ```ARPNOREMOVE``` を設定する前と後で、上記レジストリキーの内容を比較してみる。なお、このアプリの ```ProductCode``` は、```{3E990A3D-1AA9-A2B1-20A0-165054F1A654}```である。

* ARPNOREMOVE の設定前
![uninstall_reg_normal.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726086/52512af7-524d-4318-dcf7-5d8cea196f57.png)
* ARPNOREMOVE の設定後
![uninstall_reg_noremove.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726086/4babe50e-2554-ff15-72ff-0b28c79fba07.png)

設定後の内容では、```NoRemove```の値が追加され、```ModifyPath```および```UninstallString```の値が削除されているのが分かる。```ModyfyPath```については[Microsoftのドキュメント](https://docs.microsoft.com/en-us/windows/win32/msi/uninstall-registry-key)では、「*Determined and set by the Windows Installer.*」(訳：Windows Installerにより決定およびセットされる)としか書かれていない。しかし、名前と設定値から推測すると「変更」選択時に実行されるコマンドのようなので、```ARPNOREMOVE```の設定前と同じ値```MsiExec.exe /I{3E990A3D-1AA9-A2B1-20A0-165054F1A654}```を手動で設定してみると、「変更」のみを使用可能にすることができた。
![apps_noremove_modifypath.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726086/042a9af5-d41a-09be-6bf4-f13741edafdb.png)
なお、WiXにて```ModyfyPath```を設定するコードは、下記のようになる。
```xml
<RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\[ProductCode]">
	<RegistryValue Type="expandable" Name="ModifyPath" Value="MsiExec.exe /I[ProductCode]" />
</RegistryKey>
```

## さらなる問題

ここまでで「アプリと機能」で「アンインストール」ボタンだけを無効にするという目的は達成したが、こんなことをしようと思ったそもそもの動機は、アプリをアンインストールさせたくないわけではなく、必ず「変更」経由でアプリをアンインストールしてもらうためである。そこで、「アプリと機能」で「変更」ボタンをクリックし、セットアップウィザードを進め、下記の画面（メンテナンス種別選択画面と呼ぶ）を表示させる。
![maintenance_type_noremove.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726086/4a129f2d-7a6d-4866-667c-40a4b1587d5d.png)
「削除」を押してアプリをアンインストールしたいのに、「削除」ボタンが使用不可になっている。これは、WixUI（WiXの標準UIライブラリ）のメンテナンス種別選択画面も、プロパティ```ARPNOREMOVE```の値によって「削除」ボタンの有効/無効を切り変えているためである。「削除」ボタンを有効にするにはWixUIのメンテナンス種別選択画面をカスタマイズする必要があるが、その方法については別の機会に説明したい。

なお、この状態からアプリをアンインストールするには、レジストリキー```HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{ProductCode}```から値```NoRemove```を削除し、コントロールパネルの「プログラムと機能」より「アンインストール」を選択すればよい。