WiX でセットアッププロジェクト

[Microsoft Visual Studio Installer Projects](https://marketplace.visualstudio.com/items?itemName=VisualStudioClient.MicrosoftVisualStudio2017InstallerProjects) から、[WiX](https://wixtoolset.org/)に移行すべく、調べて調べて、約10日間ほどかかりました。
先人の情報に頼りつつ、それでも出てくる不明な点を解決するのに、ずいぶん時間がかかりました。
難しかったところや、日本語では書かれていない参考になる事柄を、記しておきます。

## 前提
Visual Studio 2019
WiX v3.11.2
で、セットアッププロジェクトを作成。

## 準備 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132059/31a73179-127c-20ea-ecfb-eafc59296be3.png)

* WixUIExtension.dll
* WixUtilExtension.dll

を、セットアッププロジェクトの参照に追加します。

```
C:\Program Files (x86)\WiX Toolset v3.11\bin\
```

にありますね。


## アップグレードの時に、前のバージョンを削除する
UpgradeCodeを一致させたままにして、MajorUpgrade　を行えば良いのですが、**UpgradeCodeの一致が十分条件でない**のが、はまったところです。
Installer Projectsで、インストールしたものを、WiXで作成したmsiで、削除したいのに、なぜだかできない。
InstallScopeが、前のと新しくしたいものとで異なると、前のバージョンのものだと認識出来ないのが原因でした。
.wxsのファイルから、

```
<Package  InstallScope="perMachine"
```

の、InstallScope="perMachine"を削除して、対応できました。

* [Wix to replace installations from Visual Studio installers](https://stackoverflow.com/q/18539736) 
* [How do I fix the upgrade logic of a wix setup after changing InstallScope to “perMachine”](https://stackoverflow.com/questions/678002/)

## heat.exe を使わざるえない 
NuGetで多くのDLLを参照して利用しているので、DLLのアップデートをしたら、Setupプロジェクトの、.wxsファイルも変更しなくてはいけません。
ヘルプには、セットアッププロジェクトを作成する際に、heat.exeは最初の一回行って、その後手作業で修正していくのが前提、とか書かれていますが、それでは間に合いません。
heat.exeで出力したある.wxsは、5万行を超えているので、自動化しないわけにはいきません。

そのためには、不要なフォルダ・ファイルを削除します。
削除を、heat.exeで行うには、xmlを操作するxsltファイルを作成するのがセオリーのようです。
heat.exeで利用するのに必要な、簡単なXSLTの構文を理解・作成するのに、1日がかりでした。

* DLLと一緒に、xmlファイルがついてくることがあるのですが、おそらくいらないはずなので、その削除。
* もしあるなら、不要な言語リソースを削除。
* .pdbファイルは、releaseにも含めると良いようなので、削除しません。

### XSLTについて

* [サンプルで覚えるXSLTプログラミング](https://www.atmarkit.co.jp/fxml/tanpatsu/xslt/xslt01.html)

## COM登録
DLLと、TLBをレジストリに登録しないといけないようです。
regsvr32.exeをインストーラーの中で利用するのではなく、インストーラーが直接レジストリに登録する処理を行うと良いようです。
これも、heat.exeで、引数に、hoge.dll, hoge.tlb　をそれぞれ指定して、登録する項目を集めます。

## Windowsのあるドライブ
```
<SetDirectory Id="WINDOWSVOLUME" Value="[WindowsVolume]"/>
<Directory Id="WINDOWSVOLUME">

</Directory>
```
のようにします。

* http://windows-installer-xml-wix-toolset.687559.n2.nabble.com/Error-using-WindowsVolume-predefined-folder-td3313078.html
* https://stackoverflow.com/a/1641315/9924249

[ROOTDRIVE](https://docs.microsoft.com/en-us/windows/win32/msi/rootdrive)は、最も空きスペースのあるドライブになったりするため、Windowsのあるドライブになるわけではありません。Cドライブのすぐ下にインストールしたいのに、Dドライブの下になったりしました。

## アイコン指定
```
 <Icon Id="icon.ico" SourceFile="$(var.HOGE_TargetDir)HOGE.exe"/>
```

みたいに、SourceFileに、exeを指定しても良いようです。
$(var.HOGE_TargetDir)は、パスの変数。SourceFileなので、ビルドした時に、プロジェクトフォルダのbinに作成されるexeを指定しました。

## 重複するDLL
複数のプロジェクトがソリューションにあると、重複するDLLが出ますが、1つだけ.wxsに含めれば良いようです。私の場合、最もDLLを使うプロジェクトを、heat.exeでDLLを集めるので間に合いました。そうでなければ、DLLを集めるためだけのプロジェクトを作成し、そこにソリューション全体に渡って利用するDLLをNuGetで参照してビルドで出力・heat.exeで集めるという方法があるようです。

## Orca
Orcaの利用までに手間がかかるので、
[SuperOrca](http://www.pantaray.com/msi_super_orca.html)
を利用するのも良いです。

## Directory Id="TARGETDIR" Name="SourceDir"　とは
ディレクトリの構成の最初の、

```
<Directory Id="TARGETDIR" Name="SourceDir">
```

ってなんでしょうか。

 * https://stackoverflow.com/questions/1641094/in-wix-files-what-does-name-sourcedir-refer-to

によると、.msiの内部的な処理用であって開発者は気にする必要がないようです。

## WiX Edit
WiX Editで、.msiを開くと、内部構成を確認できて、wxsファイルも出力されるので、参考になります。
Visual Studio Installer Projectsで作成した、.msiがあるなら、それを開いて、参考にするとWiXに移行しやすくなるでしょう。

## 使用許諾契約書の表示が、最初、真っ白になる
rtfによっては、表示した時に文章が何も表示されず、スクロールしたりしてから表示される不具合があります。

```
<WixVariable Id="WixUILicenseRtf" Value="license.rtf"/>
```

rtfを、WordPadで保存し直すと解決します。Wordで作成すると、複雑なrtfになるのが駄目なのかもしれません。

## ショートカットの配置で、レジストリ登録

ショートカットを配置するときに、レジストリ登録を利用します。
Nameの"installed"を、ショートカット毎に変更しないといけないのかどうか、確認したいと考えました。

```
<RegistryValue Root="HKCU" Key="Software\MyCompany\MyApplicationName" Name="installed" Type="integer" Value="1" KeyPath="yes"/>
```
[How To: Create a Shortcut on the Start Menu](https://wixtoolset.org/documentation/manual/v3/howtos/files_and_registry/create_start_menu_shortcut.html)

調べてもはっきりしなかったのは、この登録はKeyPathの確認のためだけで、アンインストール時には利用していないのでは、ということです。
複数のショートカットの配置で、同じName（"installed"）にしていますが、問題なくアンインストールされるようです。


## 32bit 64bitインストーラー
本体のプロジェクトは、Any CPUでビルドしています。そのため、Windows(64bit)なら、Program Files (x86)ではなくて、Program Filesにインストールする方法がないか調べました。
[Using Wix to create 32bit and 64bit installers from one .wxs file](https://stackoverflow.com/questions/6231950/using-wix-to-create-32bit-and-64bit-installers-from-one-wxs-file)

[Build WiX 3.6 project targeting x64?](https://stackoverflow.com/a/18630847/9924249)
「x86とx64用に2つの別々のMSIを構築する必要がある」「両方をブートストラップされたexeファイルに入れることはできる」とコメント欄にあるので、これ以上、時間をかけて調べることを止めて終了しました。

## Visual Studio Installer ProjectsのDetected Dependencies と、heat.exeで集めるDLLの数の違い

理由は分かりませんが、Installer Projectsの、Detected Dependenciesでは、プロジェクトのbinフォルダに出力されていないDLLが含まれることがあるようです。
例えば、画像の例では、System.ServiceModel.Duplex.dll～System.ServiceModel.Security.dllまで、Detected Dependenciesにはありますが、プロジェクトのbinには出力されていません。従って、heat.exeでは集められませんし、実際のアプリにも必要無いのでしょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132059/36f82cf3-857d-3c2a-44fa-db0b75c402da.png)

### 履歴
* 2020/12/28 追記