WiXはV3.11を使用。
Burnは複数のプロジェクトを一つのバンドルにまとめるもので、そのプロセルは大きくふたつに別れる。インストールを開始する前に実行するBootstarpperと、それに続く一連のインストール実行だ。
Burnを使ってインストーラを作る場合は、Bootstarpperにより必須環境を整え、そのUIによりユーザがインストール等の条件を設定し、それ以降はその条件に従い実行され、個々のmsiでは設定を変更しないのが基本のようだ。
msiにUIを表示させることはできるが、長時間かかるインストールの途中で何度もダイアログを表示するのはいただけないし、Bootstarpperが決めた条件をその後のmsiで変更できるようでは、複数のmsi間で不整合が生じうる。
また、Burnはインストール時のシーケンスを作るだけでなく、アップデート、変更、アンイストールも含めて管理するできるようになっている。そのため、Burnが作るバンドルもレジストリに登録され、コントロールパネルの「プログラムと機能」一覧に表示される。個々のmsiもここに表示することは可能だが、全てバンドルで管理できるようにしておく必要がある。
開発者のコメントもあります。
B is for Bundle and that's good enough for me.
それでも、BundleのBootstapperからオプションを設定してmsiを起動し、msiのUIでインストール/アンインストールを行うことができる。以下、多言語対応msi用のBundle作りを題材とした手順。
はじめに
試す前にレジストリのバックアップを取っておくことをお勧めする。アンインストールが適切に行なえないと、インストーラでは削除できない項目がレジストリに残ることがある。
mis作成時のProduct.wxsで、Product要素のIDを"*"としていた場合は固定のGUIDを用いてビルドし直す。"*"だと既にmsiがインストール済みか検知するときに見つからず、アンインストールできない、あるいは常にInstallしようとする、ということが発生する。
インストーラのログは次の場所に書き出される。
C:\Users\UserName\AppData\Local\Temp\
Burnによるバンドル作成
- misプロジェクトが含まれるソリューションを開く。
- ソリューション右クリック > 追加 > 新しいプロジェクト
左ペイン WiX toolset v3 > 中ペイン Bootstapper Project for WiX v3 > OK - Bootstrapper1の名前で保存
これでソリューションにBootstarpperプロジェクトが追加される。 - Bundle.wxs変更
- namespace追加
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:bal="http://schemas.microsoft.com/wix/BalExtension">
- xmlns:balは必須ではないがやがて必要になる
- Bundle要素を変更
<Bundle Name="MyAppSetup" Manufacturer="name" ... UpgradeCode="GUID">
- Name: コントロールパネルのプログラムの機能一覧に登録される名前。
exeの名前はプロジェクト > プロパティー > Output nameで設定。 - Manufacturer: 空だとエラーになる。
- UpgradeCode: msiとは別のものを設定する。
- Chain要素にMsiPackage要素を追加
<Chain> <MsiPackage SourceFile="..\SetupProject1\bin\Release\WavCutterSetup.msi" DisplayInternalUI="yes" Visible="no"> <MsiProperty Name="TRANSFORMS" Value="[TRANSFORMS]" /> </MsiPackage> </Chain>
- DisplayInternalUI="yes"の場合、msiのUIを表示(実体はmsiexecの起動パラメータ)
- Visible="yes"の場合、msiもコントロールパネルの「プログラムと機能」一覧に登録される。
- BootstrapperApplicationRef要素追加
<BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.RtfLicense">
- StandardBootstrapperApplication: 予め用意されている標準アプリ
- RtfLicenseはライセンス表示後Chain中のPackageを実行する。
- HyperlinkLicenseもあるが、試していない。
- ビルド > MyAppSetup.exe実行ライセンスに同意し、Installをクリックするとmsiが実行される。msiが再度ライセンス同意を求める。
- 「プログラムと機能」に登録上記設定の場合、コントロールパネルの「プログラムと機能」一覧にMyAppSetupが登録される。
- MyAppSetupダブルクリックMyAppSetupダブルクリックで変更/アンインストールが実行される。
「Repair」「Uninstall」はBundleとしてのものなので、msiがこれらに対応していなくてもが表示される。 - 削除実行「Uninstall」クリックで、サイレントでmsiによる削除が実行される。
MsiPackageでVisible="yes"とすると、msiも「プログラムと機能」一覧に追加されるので、これと同じことが起こる。
- クラスライブラリ追加
ソリューション右クリック > 追加 > 新しいプロジェクト > Visual C# > クラスライブラリ(.NET Framework) - プロジェクト名、アッセンブリ名=ClassLibrary1で追加。
追加するクラスはMyBootstrapper.csとする。 - 参照追加
C:\Program Files (x86)\WiX Toolset v3.11\SDK\BootstrapperCore.dll - 次のconfigファイルをBootstrapper1プロジェクトへ追加
C:\Program Files (x86)\WiX Toolset v3.11\SDK\BootstrapperCore.config
- BootstrapperCore.config 変更
<host assemblyName="ClassLibrary1"></host> - BootstrapperApplicationRef の設定を変更
<BootstrapperApplicationRef Id='ManagedBootstrapperApplicationHost'>
<Payload SourceFile="..\ClassLibrary1\bin\Release\ClassLibrary1.dll" />
<Payload Name="BootstrapperCore.config" SourceFile="BootstrapperCore.config" />
</BootstrapperApplicationRef>
<WixVariable Id="WixMbaPrereqPackageId" Value="Netfx4Full" />
<WixVariable Id="WixMbaPrereqLicenseUrl" Value="NetfxLicense.rtf" />
- WixMbaPrereqPackageId、WixMbaPrereqLicenseUrl が設定されていないとエラーになる。
- namespace行の前にアノテーション追加
[assembly:BootstrapperApplication(typeof(MySetup.MyBootstrapper))] namespace MySetup { public class MyBootstrapper : BootstrapperApplication { .... } }
- Runメソッド追加
public class MyBootstrapper : BootstrapperApplication protected overridevoid Run() { Engine.Quit(0); }
- Run()の中でUI表示や、インストール条件設定など行う。
- Engine.Quit(0)でexeの終了処理に入る。ここで実行すると実際には意味がないが、これがないとバックグランドプロセスが残るのでテスト中は適当なところでEngine.Quit(0)を呼ぶ。もし呼びそこなうと、タスクマネージャーでプロセスを終了させることになる。
- イベントハンドラ追加
少なくとも次のふたつのイベントハンドラを追加する。protected override void Run() { PlanComplete += OnPlanComplete; ApplyComplete += OnApplyComplete; if (Command.Action == LaunchAction.Install) { Engine.Plan(LaunchAction.Install); } else { //「プログラムと機能」一覧から起動した場合 Engine.Plan(LaunchAction.Uninstall); } //Engine.Quit(0)はここでは呼ばない。 } private void OnPlanComplete(object sender, PlanCompleteEventArgs e) { if (e.Status >= 0) { Engine.Apply(System.IntPtr.Zero); } else { Engine.Quit(0); } } private void OnApplyComplete(object sender, ApplyCompleteEventArgs e) { Engine.Quit(0); }