インストール後に再起動が必要になると
インストール対象のプログラムが実行中であるなどファイルが開いている場合は、アップグレードインストールやアンインストールの完了後にPCの再起動が必要になることがあります。WiX Toolsetで作成したインストーラーのデフォルトの動作では、完了画面を閉じた後、下図のような再起動を促すダイアログが表示されます。
このダイアログのUI定義はMSIファイル内には無く、OSが表示しているためカスタマイズすることができません。そこで、インストールの完了画面に再起動の機能を付けて自由にデザインできるようにしてみます。また、こうすることでユーザーが画面を進める手間を一つ減らすことができるようになります。
再起動機能付きの完了画面を作成する
そのためには、以下の機能を実装する必要があります。
- 完了画面に再起動するかどうかの選択肢を表示する
- ユーザーが再起動することを選択し、[完了]ボタンを押したらPCを再起動させる
- 完了画面が閉じた後、デフォルトの再起動を促すダイアログを表示しないようにする
- 再起動が必要なことを検出し、完了画面に反映する
完了画面に再起動するかどうかの選択肢を表示する
今回はダイアログセットのうち、WixUI_Mondoで表示するExitDialogを改造していきます。WiX ToolsetのソースからWixUI_Mondo.wxs
とExitDialog.wxs
を自分のプロジェクトのディレクトリにコピーし、プロジェクトに加えます。
ダイアログセットの名前をWixUI_Mondoから別の名前(今回はWixUI_Part25)に、ExitDialogとなっているところをすべて別の名前(今回はExitDialog2)に変更します。
そして、ダイアログセットとして新しい方の名前(今回はWixUI_Part25)を指定します。
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="E7F3756F-9431-4BE2-A62D-A65219DC415C" Name="Part25_02" Language="1033" Version="1.0.0" Manufacturer="tohshima" UpgradeCode="41d14c19-a86a-42f8-81d0-c0f377630874">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate />
<UIRef Id="WixUI_Part25"/>
<Feature Id="ProductFeature" Title="Part25_02" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="Part25_02" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component Id="cExe" Guid="{967AECBA-A823-4072-A898-B041A495D4A0}" >
<File Id="fExe" Source="C:\Windows\System32\calc.exe" KeyPath="yes" />
</Component>
</ComponentGroup>
</Fragment>
</Wix>
ここまでくればビルドできるはずですので、一旦間違いがないか確認する意味でビルドしてみます。
次に、完了画面に「完了後再起動するか、しないか」の選択肢を表示します。ユーザーが選択した結果を格納するUSERSELREBOOT
プロパティを追加し、UIエレメント内にラジオボタンのコントロール(IdはtRadioBtn)を追加します。USERSELREBOOT
プロパティにはyes_value
かno_value
が格納されます。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<!-- コントロールの初期値 -->
<Property Id="USERSELREBOOT" Value="yes_value"></Property>
<UI>
<Dialog Id="ExitDialog2" Width="370" Height="270" Title="!(loc.ExitDialog_Title)">
<Control Id="Finish" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Cancel="yes" Text="!(loc.WixUIFinish)">
<Publish Event="DoAction" Value="rebootAction">(NOT Installed) AND SHOWREBOOT="YES" AND USERSELREBOOT="yes_value"</Publish>
</Control>
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Disabled="yes" Text="!(loc.WixUICancel)" />
<Control Id="Bitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="234" TabSkip="no" Text="!(loc.ExitDialogBitmap)" />
<Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Disabled="yes" Text="!(loc.WixUIBack)" />
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
<Control Id="Description" Type="Text" X="135" Y="70" Width="220" Height="40" Transparent="yes" NoPrefix="yes" Text="!(loc.ExitDialogDescription)" />
<Control Id="Title" Type="Text" X="135" Y="20" Width="220" Height="60" Transparent="yes" NoPrefix="yes" Text="!(loc.ExitDialogTitle)" />
<Control Id="OptionalText" Type="Text" X="135" Y="110" Width="220" Height="80" Transparent="yes" NoPrefix="yes" Hidden="yes" Text="[WIXUI_EXITDIALOGOPTIONALTEXT]">
<Condition Action="show">WIXUI_EXITDIALOGOPTIONALTEXT AND NOT Installed</Condition>
</Control>
<Control Id="OptionalCheckBox" Type="CheckBox" X="135" Y="190" Width="220" Height="40" Hidden="yes" Property="WIXUI_EXITDIALOGOPTIONALCHECKBOX" CheckBoxValue="1" Text="[WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT]">
<Condition Action="show">WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT AND NOT Installed</Condition>
</Control>
<!-- 完了後再起動するかどうか設定するラジオボタン -->
<Control Id="tRadioBtn" Type="RadioButtonGroup" Property="USERSELREBOOT" X="150" Y="90" Width="180" Height="38" />
</Dialog>
<!-- 完了後再起動するかどうか設定するラジオボタンの項目 -->
<RadioButtonGroup Property="USERSELREBOOT">
<RadioButton Text="!(loc.YesRadioCaption)" Value ="yes_value" X="5" Y="0" Width="170" Height="15"/>
<RadioButton Text="!(loc.NoRadioCaption)" Value ="no_value" X="5" Y="20" Width="170" Height="15"/>
</RadioButtonGroup>
<InstallUISequence>
<Show Dialog="ExitDialog2" OnExit="success" Overridable="yes" />
</InstallUISequence>
<AdminUISequence>
<Show Dialog="ExitDialog2" OnExit="success" Overridable="yes" />
</AdminUISequence>
</UI>
</Fragment>
</Wix>
ラジオボタンの選択肢の文字列は、ローカライズのために!(loc.xxxxx)
のような書式にしてあります。ここでは文字列の定義として、日英の2か国語を定義します。プロジェクトのディレクトリに、英語用メッセージのファイルPart25_en-us.wxl
と、日本語用メッセージのファイルPart25_ja-jp.wxl
の2つを新規作成し、下記の内容にします。.wxl
ファイルの書式は、WiX Toolsetのソースのext\UIExtension\wixlib
フォルダ以下にある.wxl
ファイルが参考になります。
<?xml version="1.0" encoding="utf-8"?>
<WixLocalization Culture="en-US" Codepage="1252" xmlns="http://schemas.microsoft.com/wix/2006/localization">
<String Id="YesRadioCaption" Overridable="yes">&Yes, restart the computer now</String>
<String Id="NoRadioCaption" Overridable="yes">&No, I will restart the computer later</String>
</WixLocalization>
<?xml version="1.0" encoding="utf-8"?>
<WixLocalization Culture="ja-jp" Codepage="932" xmlns="http://schemas.microsoft.com/wix/2006/localization">
<String Id="YesRadioCaption" Overridable="yes">すぐに再起動(&Y)</String>
<String Id="NoRadioCaption" Overridable="yes">後で手動で再起動(&N)</String>
</WixLocalization>
これらのファイルをビルドに含めるには、VisualStudioのプロジェクト設定に手を加えます。まず、多言語化のために、Culture to build
設定に日英の2つを追加します。
そして、先の2つの.wxl
ファイルをプロジェクトに追加します。
これでビルドできるようになりました。実行して確認します。
ユーザーが再起動することを選択し、[完了]ボタンを押したらPCを再起動させる
「すぐに再起動」を選択した状態で[完了]ボタンを押したらPCを再起動するようにします。これは、[完了]ボタンの押下でカスタムアクションを呼び出し、PCを再起動するコマンドを実行することで実現します。再度、ExitDialog.wxsに改造を加えます。PCを再起動するカスタムアクションを加え、IdがFinishのControlエレメントの子要素にPublishエレメントを挿入し、ここでカスタムアクションを呼び出します。カスタムアクションを起動する条件は、「すぐに再起動(Y)」にチェックが入っている時です。
<!-- PCを再起動するカスタムアクション -->
<Property Id="cmdname" Value="shutdown" />
<CustomAction Id="rebootAction" Property="cmdname" ExeCommand="/r /t 0" Return="asyncNoWait" />
<UI>
<Dialog Id="ExitDialog2" Width="370" Height="270" Title="!(loc.ExitDialog_Title)">
<Control Id="Finish" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Cancel="yes" Text="!(loc.WixUIFinish)">
<Publish Event="DoAction" Value="rebootAction">USERSELREBOOT="yes_value"</Publish>
</Control>
完了画面が閉じた後、デフォルトの再起動を促すダイアログを表示しないようにする
これまでの作業でインストーラー自身がPCを再起動する機能を持つことができましたが、このままではインストール終了後に再起動マネージャーが最初に紹介したダイアログを表示してしまいます。そのため、インストーラーの完了画面でユーザーが再起動をキャンセルしても、再度再起動を促す画面になってしまいます。このダイアログを表示しないようにするには、REBOOTプロパティを使います。このプロパティに格納できる値は、下表の通りです。
値 | 意味 |
---|---|
Force | 完了画面を閉じた後に、無条件に再起動を促すダイアログを表示する。 |
Suppress | 完了画面を閉じた後に、無条件に再起動を促すダイアログを表示しない。ForceRebootアクションが発生するたびに、インストール中に再起動するオプションを表示する。 |
ReallySuppress | 完了画面を閉じた後に、無条件に再起動を促すダイアログを表示しない。ForceRebootアクションが発生しても、インストール中に再起動するオプションも表示しない。 |
余計なダイアログを表示せず、最後に自前で再起動を促すようにしたいので、ReallySuppressを使用します。
<Property id="REBOOT" value="ReallySuppress">
再起動が必要なことを検出し、完了画面に反映する
完了画面を改造することで、インストーラーがPCを再起動できるようになりましたが、このままだと再起動が必要ない時にもユーザーが再起動させることができてしまいます。再起動が必要なほとんどのケースは、FileInUseダイアログでファイルを使用中のプログラムを終了しなかった場合です。そこでFileInUseダイアログで「アプリケーションを終了しない(再起動が必要)」を選択したことを検出して、完了画面のUI表示に反映させます。
OrcaでMSIのRadioButtonテーブルを確認すると、このラジオボタンを選択した結果はWixUIRMOptionプロパティにUseRMまたはDontUseRMという値で格納されることがわかります。
そこで、このプロパティの値がDontUseRMだったときに完了画面に再起動の選択肢を出すように変更します。このためには、FileInUseダイアログで[OK]ボタンを押した時、判別用のプロパティに値をセットするような実装を行います。そのために、改造用にFilesInUse.wxsファイルをコピーしてくる必要はなく、すでにコピーしてきたWixUI_Mondo.wxsファイルを改造して済ませることができます。WixUI_Mondo.wxsファイルのUIエレメントに下記の1行を加えます。
<Publish Dialog="MsiRMFilesInUse" Control="OK" Property="SHOWREBOOT" Value="YES">WixUIRMOption="DontUseRM"</Publish>
こうすると、FileInUseダイアログで[OK]ボタンを押した時、「アプリケーションを終了しない(再起動が必要)」が選択されていると、SHOWREBOOTプロパティにYES
が格納されます。ExitDialog.wxsファイルでは、このSHOWREBOOTプロパティを使ってラジオボタンの表示・非表示を制御します。
<!-- 完了後再起動するかどうか設定するラジオボタン -->
<Control Id="tRadioBtn" Type="RadioButtonGroup" Property="USERSELREBOOT" X="150" Y="90" Width="180" Height="38" Hidden="yes">
<Condition Action="show">SHOWREBOOT="YES"</Condition>
</Control>
ControlエレメントにHidden="yes"
を加えてやれば、FileInUseダイアログで「アプリケーションを終了しない(再起動が必要)」が選択されているときだけ、再起動するかどうかを選択するラジオボタンを表示できるようになります。そして、このラジオボタンが表示されているときだけ再起動させたいので、PCを再起動するアクションの条件にもSHOWREBOOTプロパティを加えます。
<Control Id="Finish" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Cancel="yes" Text="!(loc.WixUIFinish)">
<Publish Event="DoAction" Value="rebootAction">SHOWREBOOT="YES" AND USERSELREBOOT="yes_value"</Publish>
</Control>
最終的に、ExitDialog.wxsファイルとWixUI_Mondo.wxsファイルは下記のようになります。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<!-- コントロールの初期値 -->
<Property Id="USERSELREBOOT" Value="yes_value"></Property>
<!-- PCを再起動するカスタムアクション -->
<Property Id="cmdname" Value="shutdown" />
<CustomAction Id="rebootAction" Property="cmdname" ExeCommand="/r /t 0" Return="asyncNoWait" />
<!-- 再起動マネージャーの再起動要求ダイアログの表示を抑止 -->
<Property Id="REBOOT" Value="ReallySuppress"/>
<UI>
<Dialog Id="ExitDialog2" Width="370" Height="270" Title="!(loc.ExitDialog_Title)">
<Control Id="Finish" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Cancel="yes" Text="!(loc.WixUIFinish)">
<Publish Event="DoAction" Value="rebootAction">SHOWREBOOT="YES" AND USERSELREBOOT="yes_value"</Publish>
</Control>
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Disabled="yes" Text="!(loc.WixUICancel)" />
<Control Id="Bitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="234" TabSkip="no" Text="!(loc.ExitDialogBitmap)" />
<Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Disabled="yes" Text="!(loc.WixUIBack)" />
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
<Control Id="Description" Type="Text" X="135" Y="70" Width="220" Height="40" Transparent="yes" NoPrefix="yes" Text="!(loc.ExitDialogDescription)" />
<Control Id="Title" Type="Text" X="135" Y="20" Width="220" Height="60" Transparent="yes" NoPrefix="yes" Text="!(loc.ExitDialogTitle)" />
<Control Id="OptionalText" Type="Text" X="135" Y="110" Width="220" Height="80" Transparent="yes" NoPrefix="yes" Hidden="yes" Text="[WIXUI_EXITDIALOGOPTIONALTEXT]">
<Condition Action="show">WIXUI_EXITDIALOGOPTIONALTEXT AND NOT Installed</Condition>
</Control>
<Control Id="OptionalCheckBox" Type="CheckBox" X="135" Y="190" Width="220" Height="40" Hidden="yes" Property="WIXUI_EXITDIALOGOPTIONALCHECKBOX" CheckBoxValue="1" Text="[WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT]">
<Condition Action="show">WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT AND NOT Installed</Condition>
</Control>
<!-- 完了後再起動するかどうか設定するラジオボタン -->
<Control Id="tRadioBtn" Type="RadioButtonGroup" Property="USERSELREBOOT" X="150" Y="90" Width="180" Height="38" Hidden="yes">
<Condition Action="show">SHOWREBOOT="YES"</Condition>
</Control>
</Dialog>
<!-- 完了後再起動するかどうか設定するラジオボタンの項目 -->
<RadioButtonGroup Property="USERSELREBOOT">
<RadioButton Text="!(loc.YesRadioCaption)" Value ="yes_value" X="5" Y="0" Width="170" Height="15"/>
<RadioButton Text="!(loc.NoRadioCaption)" Value ="no_value" X="5" Y="20" Width="170" Height="15"/>
</RadioButtonGroup>
<InstallUISequence>
<Show Dialog="ExitDialog2" OnExit="success" Overridable="yes" />
</InstallUISequence>
<AdminUISequence>
<Show Dialog="ExitDialog2" OnExit="success" Overridable="yes" />
</AdminUISequence>
</UI>
</Fragment>
</Wix>
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<UI Id="WixUI_Part25">
<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="Mondo" />
<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="MsiRMFilesInUse" Control="OK" Property="SHOWREBOOT" Value="YES">WixUIRMOption="DontUseRM"</Publish>
<Publish Dialog="ExitDialog2" Control="Finish" Event="EndDialog" Value="Return" Order="999">1</Publish>
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="LicenseAgreementDlg">NOT Installed AND NOT PATCH</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="SetupTypeDlg" Order="2">LicenseAccepted = "1"</Publish>
<Publish Dialog="SetupTypeDlg" Control="Back" Event="NewDialog" Value="LicenseAgreementDlg">1</Publish>
<Publish Dialog="SetupTypeDlg" Control="TypicalButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
<Publish Dialog="SetupTypeDlg" Control="CustomButton" Event="NewDialog" Value="CustomizeDlg">1</Publish>
<Publish Dialog="SetupTypeDlg" Control="CompleteButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
<Publish Dialog="CustomizeDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="1">WixUI_InstallMode = "Change"</Publish>
<Publish Dialog="CustomizeDlg" Control="Back" Event="NewDialog" Value="SetupTypeDlg" Order="2">WixUI_InstallMode = "InstallCustom"</Publish>
<Publish Dialog="CustomizeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="CustomizeDlg" Order="1">WixUI_InstallMode = "InstallCustom"</Publish>
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="SetupTypeDlg" Order="2">WixUI_InstallMode = "InstallTypical" OR WixUI_InstallMode = "InstallComplete"</Publish>
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="CustomizeDlg" Order="3">WixUI_InstallMode = "Change"</Publish>
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="4">WixUI_InstallMode = "Repair" OR WixUI_InstallMode = "Remove"</Publish>
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="2">WixUI_InstallMode = "Update"</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>