Windows installer deletes versioned file during product upgrade, instead of downgrading it
Asked Answered
D

6

33

We use wix to create our setups. For upgrading, we use major upgrades as demonstrated in this answer by Rob Mensching. (In newer wix versions you can use the MajorUpgrade element.) This normally works well. The old product is removed, then the new product is installed.

However, apparently the above is not completely equivalent to manually uninstalling the old product and then manually installing the new product.

Consider for example the following scenario:

  • version 1.0 of our product is released, containing version 5.0 of a thirdparty dll
  • version 1.1 of our product is released, containing version 5.1 of the same thirdparty dll
  • version 1.2 of our product is released, downgrading to version 5.0 of the thirdparty dll again because we discovered that the new version introduced more problems than it solved.

Apparently with the wix upgrade logic linked above, the 3rdparty dll will disappear when upgrading from release 1.1 to 1.2. A repair is necessary to restore it.

Is there another way to upgrade, which would work for this scenario? I guess what I am looking for is upgrade logic which allows the downgrading of components, by behaving exactly as if one manually uninstalls the old product and then manually installs the new product.

Deadwood answered 19/11, 2010 at 17:1 Comment(0)
W
9

We also encountered this problem where lower-versioned DLLs were not getting reinstalled on a major upgrade. I thought it was strange that the installer would decide which files to install based on the versioning of existing files, then completely uninstall everything, but still only install what what files had been determined to install before uninstalling the old product. This seems like it might be a bug in Windows Installer...

To fix this problem we moved the RemoveExistingProducts action above the CostFinalize action.

I know the documentation on MSDN recommends placing the RemoveExistingProducts afterInstallValidate, and I'm not sure if putting it before the InstallValidate action has any negative side effects for minor upgrades, but we have decided to only perform major upgrades for our products so this solution appears to work for us.

Wert answered 13/6, 2013 at 17:49 Comment(6)
How did you get away with that? If I try to do this on our installer, I get this error: ICE27: 'RemoveExistingProducts' Action in InstallExecuteSequence table in wrong place. Current: Costing, Correct: ExecutionAtheist
We are using InstallShield to build our installs, and it has allowed us to put the actions in this order. Perhaps whatever application you're using to build your installers does some kind of verification check for a certain order and doesn't allow this sequence. If so, there may be some way of disabling this check, but I don't know if it would be recommended.Wert
A quick Google search let me know that the ICE stands for Internal Consistency Evaluator, and this must be running during your installer build. To get around it, you could build the installer with the normal sequence and then open the MSI with a tool like Instedit and manually change the sequence values in the InstallExecuteSequence table.Wert
@Atheist actually, looking at the sequence. We moved the RemoveExistingProducts before CostInitialize as well. It may not be valid to insert it between the costing actions.Wert
@Atheist Okay, this MSDN article explains valid placement of the RemoveExistingProducts action. msdn.microsoft.com/en-us/library/aa371197(v=vs.85).aspx. -- One of the options was 'Between the InstallValidate action and the InstallInitialize action. In this case, the installer removes the old applications entirely before installing the new applications. This is an inefficient placement for the action because all reused files have to be recopied.'Wert
@Wert Wouldn't this solution cause feature states to not be migrated on a silent installation with no UI? Moving REP action to before costing means it also moves before MigrateFeatureStates. If a UI is used to install, there's no consequence because MigrateFeatureStates has already run and will be skipped. However, if the InstallUISequence is not run, I'd think that MigrateFeatureStates would actually run in InstallExecuteSequence (vs being skipped), and then not find any feature state to migrate from the old version that was already uninstalled.Kaitlin
N
4

Behaviors like this generally have to do with the sequencing of RemoveExistingProducts. If it occurs late enough, Windows Installer will have figured out that there's a newer version of the .dll on the machine, so version 1.2 doesn't need to install it. However when the RemoveExistingProducts results in removing the .dll, nothing puts it back.

Things to try including changing the sequencing of RemoveExistingProducts, and lying about the version of the .dll in your 1.2 package (report a version number higher than the bad one). The downside of the latter is poor impacts on repairs or patching, as the .dll always looks out of date.

Natterjack answered 20/11, 2010 at 16:37 Comment(7)
Scheduling RemoveExistingProducts after InstallFinalize has another undesired effect: the dll is kept at the newer version. I also considered overriding the file version in the setup, but this has the unfortunate consequence that I will have to manually update that fake version number each time the DLL changes :-(Deadwood
Why would you have to keep updating it? Wasn't there just a specific version which doesn't work, and you're overriding that so the one previous to it will reinstall? Perhaps lie that it is exactly the newer version and set REINSTALLMODE=emus.Natterjack
The problem already occurs during costing. At this point, MSI sees that a newer version is present, and decides not to install the new file. You'll see Disallowing installation of component: XXX since the same component with higher versioned keyfile exists in the log. So you'd have to schedule RemoveExistingProducts before costing. I don't see how that could be done without violating ICE27 and ICE63.Diplomacy
For what it's worth, some ICEs seem to be overzealous. That said, any scenario wherein you need to downgrade a file according to its version, at least using Windows Installer, is going have repercussions.Natterjack
For a nice writeup on this, see Aaron Stebner's recent blog post: Why Windows Installer removes files during a major upgrade if they go backwards in version numbersNatterjack
@MichaelUrman the post is goneMonkeypot
@johnktejik Found it by searching for "Aaron Stebner blog" and following the 2015-11-16 date from the original URL.Natterjack
B
1

Try to schedule RemoveExistingProducts earlier, right after InstallValidate, and change the value of REINSTALLMODE property to amus. This way the old product will be completely removed before any files from the new product are copied, and a mode will force re-install of the files.

Boong answered 29/7, 2011 at 12:59 Comment(4)
This answer is not helpful. The suggested placement of RemoveExistingProducts after InstallValidate doesn't help. Setting REINSTALLMODE to amus is very dangerous because a force re-installs ALL files, including shared system files that you might want to not downgrade. For example, imagine you have some merge modules that install system runtime DLLs. These DLLs are later patched with a security patch by the vendor. When your package is later installed with amus, you'll overwrite the newer, safer DLLs from the vendor.Kaitlin
@JamesJohnston so what would you do instead? Assuming you don't have merge modules installing system-wide DLL's, amus really seems like the only feasible solution I've been able to find. (But I'd love to hear of a better solution)Atheist
@jalf: after an extensive search, I came to the conclusion that REINSTALLMODE=amus is the only sensible solution. However, it can only be used if there are no shared components in your instal.Diplomacy
amus will ruin your life if you have shared components between products.Pily
E
1

Years later, this thread helped me in the right direction. An example for completeness with RemoveExisitingProducts moved before costing:

<Upgrade Id="UPGRADE-GUID-HERE">
    <UpgradeVersion OnlyDetect="no" Property="UPGRADABLEFOUND"
        Maximum="$(var.ProductVersion)" IncludeMaximum="yes" />
    <UpgradeVersion OnlyDetect="yes" Property="NEWERFOUND"
        Minimum="$(var.ProductVersion)" IncludeMinimum="no" />
</Upgrade>

<InstallExecuteSequence>
    <Custom Action="NoDowngrade" After="FindRelatedProducts">NEWERFOUND</Custom>
    <RemoveExistingProducts Before="CostInitialize" />
</InstallExecuteSequence>

<CustomAction Id="NoDowngrade" Error="A newer version of $(var.ProductName) is already installed." />
Endostosis answered 27/3, 2019 at 17:41 Comment(0)
P
0

It's sub-optimal, but I fixed the same problem by renaming the third party dll and changing the GUID on the component node associated with it in the .wxs file.

Pattern answered 28/3, 2013 at 13:46 Comment(0)
D
0

Here's my final solution based on the answer given by @Spacemani.

It produces MSI table entries (Upgrade, LaunchCondition etc.) similar to this

<MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeErrorMessage)" />

but gives you full control of the InstallExecuteSequence.

<!-- Product upgrade -->
<Upgrade Id="$(var.UpgradeCode)">
  <UpgradeVersion OnlyDetect="no" Property="WIX_UPGRADE_DETECTED"
                  Maximum="$(var.ProductVersion)" IncludeMaximum="no" IncludeMinimum="no"
                  MigrateFeatures="yes" />
  <UpgradeVersion OnlyDetect="yes" Property="WIX_DOWNGRADE_DETECTED"
                  Minimum="$(var.ProductVersion)" IncludeMinimum="no" />
</Upgrade>
<InstallExecuteSequence>
  <RemoveExistingProducts Before="CostInitialize" />
</InstallExecuteSequence>
<Condition Message="!(loc.DowngradeErrorMessage)">NOT WIX_DOWNGRADE_DETECTED</Condition>

Note that you need to suppress ICE27 errors in your .wixproj file like this.

<PropertyGroup>
  <SuppressIces>ICE27</SuppressIces>
</PropertyGroup>
Discourage answered 19/6, 2019 at 12:3 Comment(1)
what is !(loc.DowngradeErrorMessage)? Do you have any documentation on this?Monkeypot

© 2022 - 2024 — McMap. All rights reserved.