mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-28 12:07:48 -06:00
fix(installer): resolve the wizard scope and folder correctly
Real msiexec runs (per-user and all-users, silent and wizard) found two defects that the metadata fixtures cannot see: - "Only for me" published ALLUSERS=2, so FindRelatedProducts treated the run as per-machine and skipped the existing per-user copy. An interactive upgrade left two registered products and uninstall left files behind. The page now leaves ALLUSERS unset for a per-user choice. - Windows Installer resolves Program Files once, for the context it started in, so choosing "Everyone" in the wizard installed into the user profile with a machine-wide registration. The wizard now sets the new-install folder itself after the choice (Program Files, Program Files (x86) for 32-bit, or the per-user Programs folder). Silent installs are unchanged. check-install-policy.ps1 now fails if either regresses.
This commit is contained in:
@@ -140,6 +140,28 @@ try {
|
||||
$ui['CraftScopeDlg'] -lt $ui['CraftRestoreUserFolder'] -and $ui['CraftRestoreUserFolder'] -lt $ui['CostInitialize'])) {
|
||||
throw 'Scope must be chosen after AppSearch and before FindRelatedProducts, launch checks and costing.'
|
||||
}
|
||||
# Windows Installer resolves Program Files for the context it started in, before the wizard's scope choice.
|
||||
# The wizard therefore sets the new-install folder itself, and only in the wizard.
|
||||
$defaults = @(Read-Rows $database 'SELECT `Action`, `Sequence`, `Condition` FROM `InstallUISequence`' 3 | Where-Object { $_[0] -like 'CraftDefault*' })
|
||||
if ($defaults.Count -lt 2) { throw 'The wizard must set the new-install folder after the scope page; Program Files is resolved before the choice.' }
|
||||
foreach ($row in $defaults) {
|
||||
if (-not ($ui['CraftScopeDlg'] -lt [int]$row[1] -and [int]$row[1] -lt $ui['CostInitialize'])) { throw "$($row[0]) must run after the scope page and before costing." }
|
||||
}
|
||||
if (@(Read-Rows $database 'SELECT `Action` FROM `InstallExecuteSequence`' 1 | Where-Object { $_[0] -like 'CraftDefault*' }).Count) {
|
||||
throw 'Silent installs must not use the wizard-only default folder actions.'
|
||||
}
|
||||
foreach ($scope in @('1', '')) {
|
||||
Set-SessionProperty $session 'ALLUSERS' $scope
|
||||
Set-SessionProperty $session 'INSTALLFOLDER' ''
|
||||
Set-SessionProperty $session 'CRAFT_MACHINE_DIR' ''
|
||||
Set-SessionProperty $session 'CRAFT_USER_DIR' ''
|
||||
foreach ($row in @($defaults | Sort-Object { [int]$_[1] })) {
|
||||
if ($session.EvaluateCondition($row[2]) -eq 1 -and $session.DoAction($row[0]) -ne 1) { throw "$($row[0]) failed." }
|
||||
}
|
||||
$folder = Get-SessionProperty $session 'INSTALLFOLDER'
|
||||
$wanted = if ($scope -eq '1') { '^[A-Za-z]:\\Program Files( \(x86\))?\\ArtCraft\\[^\\]+\\$' } else { '^' + [regex]::Escape($env:LOCALAPPDATA) + '\\Programs\\ArtCraft\\[^\\]+\\$' }
|
||||
if ($folder -notmatch $wanted) { throw "Wizard default folder for ALLUSERS='$scope' is '$folder'." }
|
||||
}
|
||||
$execute = @{}
|
||||
foreach ($row in (Read-Rows $database 'SELECT `Action`, `Sequence` FROM `InstallExecuteSequence`' 2)) { $execute[$row[0]] = [int]$row[1] }
|
||||
foreach ($action in @('CraftRestoreUserFolder','CraftRestoreMachineFolder')) {
|
||||
@@ -177,7 +199,7 @@ try {
|
||||
$allUsers = Get-SessionProperty $session 'ALLUSERS'
|
||||
$perUser = Get-SessionProperty $session 'MSIINSTALLPERUSER'
|
||||
if (($choice -eq 'machine' -and ($allUsers -ne '1' -or $perUser)) -or
|
||||
($choice -eq 'user' -and ($allUsers -ne '2' -or $perUser -ne '1'))) {
|
||||
($choice -eq 'user' -and ($allUsers -or $perUser -ne '1'))) {
|
||||
throw 'Scope selector does not reset the requested installation context.'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Include xmlns="http://wixtoolset.org/schemas/v4/wxs">
|
||||
<!-- Choose scope before launch checks and directory costing. Never move an existing copy. -->
|
||||
<!-- Only for me leaves ALLUSERS unset. Publishing ALLUSERS=2 here makes FindRelatedProducts treat the run as
|
||||
per-machine and skip the existing per-user copy, so an interactive upgrade installs beside it
|
||||
(two registered products, and uninstall leaves files behind). Checked with real msiexec runs. -->
|
||||
<Property Id="INSTALLFOLDER" Secure="yes" />
|
||||
<Property Id="CRAFT_SCOPE" Value="user" Secure="yes" />
|
||||
<Property Id="CRAFT_U_DIR" Secure="yes"><RegistrySearch Id="CRAFT_U_DIRSearch" Root="HKCU" Key="Software\EffectCraft" Name="InstallDir" Type="raw" /></Property>
|
||||
@@ -25,6 +28,16 @@
|
||||
<Property Id="CRAFT_MACHINE_DIR" Secure="yes" />
|
||||
<SetProperty Id="CRAFT_SCOPE" Action="CraftPreferMachineScope" Value="machine" After="CraftMFromLegacyExe" Sequence="ui" Condition="ALLUSERS = 1 OR (CRAFT_MACHINE_DIR AND NOT CRAFT_USER_DIR)" />
|
||||
<SetProperty Id="INSTALLFOLDER" Action="CraftRestoreUserFolder" Value="[CRAFT_USER_DIR]" Before="CostInitialize" Sequence="both" Condition="NOT INSTALLFOLDER AND ALLUSERS <> 1 AND CRAFT_USER_DIR" />
|
||||
<!-- The wizard can change the context after Windows Installer has already resolved Program Files for the first one.
|
||||
Give the new-install folder explicitly so Everyone never lands in the profile and Only for me never lands in Program Files. -->
|
||||
<?if $(sys.BUILDARCH) = x86 ?>
|
||||
<SetProperty Id="INSTALLFOLDER" Action="CraftDefaultMachineFolder" Value="[%ProgramFiles(x86)]\ArtCraft\EffectCraft\" After="CraftRestoreUserFolder" Sequence="ui" Condition="NOT INSTALLFOLDER AND ALLUSERS = 1 AND VersionNT64" />
|
||||
<SetProperty Id="INSTALLFOLDER" Action="CraftDefaultMachineFolder32" Value="[%ProgramFiles]\ArtCraft\EffectCraft\" After="CraftDefaultMachineFolder" Sequence="ui" Condition="NOT INSTALLFOLDER AND ALLUSERS = 1 AND NOT VersionNT64" />
|
||||
<SetProperty Id="INSTALLFOLDER" Action="CraftDefaultUserFolder" Value="[LocalAppDataFolder]Programs\ArtCraft\EffectCraft\" After="CraftDefaultMachineFolder32" Sequence="ui" Condition="NOT INSTALLFOLDER AND ALLUSERS <> 1" />
|
||||
<?else?>
|
||||
<SetProperty Id="INSTALLFOLDER" Action="CraftDefaultMachineFolder" Value="[%ProgramW6432]\ArtCraft\EffectCraft\" After="CraftRestoreUserFolder" Sequence="ui" Condition="NOT INSTALLFOLDER AND ALLUSERS = 1" />
|
||||
<SetProperty Id="INSTALLFOLDER" Action="CraftDefaultUserFolder" Value="[LocalAppDataFolder]Programs\ArtCraft\EffectCraft\" After="CraftDefaultMachineFolder" Sequence="ui" Condition="NOT INSTALLFOLDER AND ALLUSERS <> 1" />
|
||||
<?endif?>
|
||||
<SetProperty Id="INSTALLFOLDER" Action="CraftRestoreMachineFolder" Value="[CRAFT_MACHINE_DIR]" Before="CraftRestoreUserFolder" Sequence="both" Condition="NOT INSTALLFOLDER AND ALLUSERS = 1 AND CRAFT_MACHINE_DIR" />
|
||||
<Launch Condition="Installed OR ALLUSERS <> 1 OR NOT CRAFT_USER_DIR" Message="This app is already installed for your account. Choose Only for me to update it, or uninstall that copy before changing who it is installed for." />
|
||||
<Launch Condition="Installed OR ALLUSERS = 1 OR NOT CRAFT_MACHINE_DIR" Message="This app is already installed for everyone. Choose Everyone to update it, or pass ALLUSERS=1 to msiexec. Uninstall that copy before changing who it is installed for." />
|
||||
@@ -42,7 +55,7 @@
|
||||
</Control>
|
||||
<Control Id="Next" Type="PushButton" X="214" Y="182" Width="66" Height="20" Default="yes" Text="Next">
|
||||
<Publish Property="ALLUSERS" Value="1" Order="1" Condition="CRAFT_SCOPE = "machine"" />
|
||||
<Publish Property="ALLUSERS" Value="2" Order="2" Condition="CRAFT_SCOPE = "user"" />
|
||||
<Publish Property="ALLUSERS" Value="{}" Order="2" Condition="CRAFT_SCOPE = "user"" />
|
||||
<Publish Property="MSIINSTALLPERUSER" Value="{}" Order="3" Condition="CRAFT_SCOPE = "machine"" />
|
||||
<Publish Property="MSIINSTALLPERUSER" Value="1" Order="4" Condition="CRAFT_SCOPE = "user"" />
|
||||
<Publish Event="EndDialog" Value="Return" Order="5" />
|
||||
|
||||
Reference in New Issue
Block a user