Using Minor Upgrade to deliver a few new files - wix

I am doing a PoC that involves creating an MSI that has version 1.0.0.0; installing that version to a test machine.
Next, I create another MSI (same name, same product code, different package code, same upgrade code). I merely added a single new .txt file to the sources of the original (v1.0.0.0) MSI. I create the new MSI file using WiX and give it version 1.1.0.0.
All good so far.
Here's a breakdown of the codes so far (from Orca.exe):
ProductCode for msi-v1.0.0.0: {CBCF9206-1539-47B8-9A46-A18C2E40D7A1}
ProductCode for msi-v1.1.0.0: {CBCF9206-1539-47B8-9A46-A18C2E40D7A1}
PackageCode for msi-v1.0.0.0: {AB2B02E4-213E-48C1-9394-E30A75BAF2BE}
PackageCode for msi-v1.1.0.0: {C68D3A88-583A-41BF-A971-CB5E083B8547}
UpgradeCode for msi-v1.0.0.0: {06726F10-FF0B-4534-A008-032A70CACDBB}
UpgradeCode for msi-v1.1.0.0: {06726F10-FF0B-4534-A008-032A70CACDBB}
ProductVersion for msi-v1.0.0.0: 1.0.0.0
ProductVersion for msi-v1.1.0.0: 1.1.0.0
What I am trying to accomplish is the deployment of that new single .txt file via this Minor Upgrade. I know that there is a smaller type update called Small Update, but that is not where this PoC is headed. We will need to change the version number as a part of our end game.
I have this in the Wix script that is used to gen both the MSIs (I really don't think this has anything to do with my issue - just including it because it has the word 'Upgrade' in it):
<MajorUpgrade
DowngradeErrorMessage="A later version of [ProductName] is already installed. Setup will now exit."
AllowDowngrades="no"
AllowSameVersionUpgrades="yes"
/>
What I am seeing is that when I run:
msiexec.exe /i FileName.msi REINSTALLMODE=vomus REINSTALL=ALL
I do not get the new single .txt file delivered. I do see that the product version (in appwiz.cpl) changes from 1.0.0.0 to 1.1.0.0, and the cached local MSI file (under C:\Windows\Installer dir) is indeed now version 1.1.0.0 (verified by Orca.exe).
I am puzzled as to why the new single .txt file is not being deployed.
I guess my primary question is: Why won't this Minor Upgrade (i.e. same product code, diff package code, diff product version) deliver the new file?
Thanks in advance for any pointers!

If you broke the component rules you'll see something in the log about it. There'll be SELMGR entries and something about removing components being unsupported. That might happen if you didn't add the file correctly. If you do the minor update install with MSIENFORCEUPGRADECOMPONENTRULES set to 1 on the command line it will fail the install if you broke the rules.

I would read:
What happens if the component rules are broken?
and
Dealing with very large number of files
I'm not a huge opponent of this type of automation. Especially if you are trying to do minor upgrades and patches. Far too often I find people who learn a little bit about installers, don't really want to do it and find it more interesting and "automated" to automate the automation. Don't do it! :) Instead I focus on creating processes that make installer development and maintenance as easy as possible. (See IsWiX on CodePlex) Only the experienced developer knows his code and can make the right choices on how to deploy his resources.

Related

Making changes to installed MSI

I will try to explain the issue by example. I have mypackage-v1.0.msi and mypackage-v1.1.msi. Both packages when installed silently expecting PASSWORD parameter:
msiexec /i mypackage-v1.0.msi /qb PASSWORD=SomeThing1
mypackage-v1.0.msi is already installed and mypackage-v1.1.msi should upgrade mypackage-v1.0.msi. There is a custom action in both packages that depends on this parameter but because of the issue with conditioning this custom action in mypackage-v1.0.msi, during upgrade it is executed but the PASSWORD parameter is not transferred to it.
I wonder if there is a way to patch mypackage-v1.0.msi before upgrading to mypackage-v1.1.msi. But the patch is not changing any contents of installation but the package itself. Is it possible?
Edit:
More focused input - is it possible with the patch to replace a condition for InstallExecuteSequence custom actions?
Yes. Patches include transforms, which modify packages. In fact, patches always modify tables in the package, even when they don't change content of the payload files.
The comments above became too messy. Here is a quick re-write as a regular answer:
Minor Upgrade: A minor upgrade can change most things in an installed MSI package before its uninstall or upgrade sequence is called. It can hence fix problems relating to major upgrades or failed uninstalls among other things (you fix the uninstall sequence before it is invoked).
Sample: Here is the simplest sample of a minor upgrade I could find at the moment (another sample from FireGiant's documentation).
For the simple sample: open the CreatePatch.cmd and update path to WiX binaries (likely ending in v3.11 at this point in time: set wixDir="C:\Program Files (x86)\WiX Toolset v3.11\bin\").
Keep in mind that a minor upgrade has many limitations. Keep things simple and change only what you need to fix your problem.
Minor Upgrade Details: The table at the bottom here shows what needs to change for a minor upgrade. Essentially package code and product version + plus whatever change you want to implement. The above link is to InstallShield's documentation (a different MSI tool), but this is a generic MSI technology concept - it is not vendor specific.
You can deliver the minor upgrade as a new MSI or as a patch file (*.msp). For an MSI you need to use a special command line to install. Something like this:
msiexec.exe /i MySetup.msi REINSTALLMODE=vomus REINSTALL=ALL
The v tells msiexec to re-cache the MSI so that it updates the existing cached one "in-place".
REINSTALLMODE documentation.
Applying Small Updates by Reinstalling the Product.
Links:
https://support.firegiant.com/hc/en-us/articles/230912367-Upgrade-options
https://www.firegiant.com/wix/tutorial/upgrades-and-modularization/

File of a new component isn't installed because there was an old component with the same file

We have a problem that a fie is not installed upon a major update
We have a major update with <MajorUpgrade Schedule="afterInstallInitialize"...
We an old component with 1 file (xyz.exe Version 12.34) from a external manufacturer
We have now a new file from a new manufacturer and with the same name (xyz.exe Version 2.34). The new file has a lower version number than the old one.
We created a new component in the install package and removed the old component (in fact we gave it a new guid)
Changing the name of the exe isn't possible, it would have to much influence upon documentation and internal functions.
On a normal installation everything is OK.
But what happens now on an update:
The installer starts.
And detects that the new component exists (xyz.exe) with a lower version, so it will not be installed.
the installer runs and removes the old component
But it doesn't install the new because it just detected that the component was already installed.
Doing a repair installation fixes the problem and the file is than again present.
Setting the KeyPath to the Component fixes the problem. But it seams wrong to me. The directory where this file is installed is the main installation directory.
How to force the installation of this component?
Similar Answer: How to Explicitly Remove dll During Majorupgrade Using Wix Toolset
Major Upgrade Downgrade: In order to overwrite binaries with higher version numbers on major upgrades there are a couple of preferred options:
The preferred approach would be to use a companion file (third party files).
Or if you can: compile a new binary with a higher version number (for your own files).
Companion Files: A snippet below on how to use companion files in WiX:
<..>
<Component Id="MyFile.exe" Feature="Main">
<File Id="MyFile.exe" Source="MyFile.exe"></File>
</Component>
<!-- Do not re-use any GUIDs in your own sources! (very important) -->
<Component Id="MyFile_2.exe" Guid="{00000000-0000-0000-0000-3D82EA2A99AF}" Feature="Main">
<File Source="MyFile_2.exe" CompanionFile="MyFile.exe"></File>
</Component>
<..>
One-Line Summary: In the second component we point to the first component's file so that MyFile_2.exe will install whenever MyFile.exe is installed - regardless of versioning issues.
Then there are a number of further options:
REINSTALLMODE: The MSI property REINSTALLMODE can be used - but it has a number of side-effects:
Setup 1: Version 1.0.0 for a setup:
msiexec.exe /i Setup1.msi /qn
Setup 2: Version 2.0.0 for the major upgrade setup:
msiexec.exe /i Setup2.msi REINSTALLMODE=amus /qn
Several Problems: There are several issues with REINSTALLMODE that makes it an unsafe feature to use (try emus instead? See documentation - a little less brute force maybe). It is a shame that this setting applies to all features in the setup - that makes it very dangerous:
can downgrade shared files system-wide - if there are merge modules included - for example (features in Windows are in place to prevent most of this problem: WFP and WRP in Vista an beyond - non-Microsoft merge modules can still cause problems for non-Microsoft shared files)
can cause inconsistent version estate since an old package can be installed after a newer one and downgrade only some of the shared files
can downgrade or wipe-out settings in non-versioned files and registry settings (note to self: test this again, there are complexities with component settings)
can cause a significant increase in the number of requested reboots due to attempts to needlessly replace in-use files of the same version (the real fix for this is to shut down services properly and to use the restart manager to allow applications to be shut down automatically during deployment - on file locks).
there are several further issues that are quite specific
Hack Binary Version: An ugly, but effective option is to change the version of the actual binary file using Visual Studio to set a higher version number (you open the binary as a resource and set a new version - this is obviously very different from compiling a new version of the binary using visual studio source code compilation). There are several side effects:
you break digital signatures
you can create "version confusion"
there are risks involved writing a new binary from Visual Studio
it is a "hack manual step" - you might need to keep doing this for new versions?
etc...
Move, Rename: If you can de-couple the new file from the old by renaming it or moving it you can work around the problem. If you get a new version again for the future, you might have to do this again. Clunky.
"Load From": Putting the file somewhere shared and load it from that specific location and removing the old copy from your installation folder. Could that work? This means the file could also be delivered by another setup at that location.
Version Lying: In Installshield there is a concept of being able to set a specific version number to a file. I am not sure how to implement that in WiX. There is also an "always overwrite option" that apparently sets a maximum value for the version so the existing file is always overwritten.
Some Links:
Why Windows Installer removes files during a major upgrade if they go backwards in version numbers
"Downgraded" MS dll disappears on upgrade - Windows Installer
Install a file regardless of version number with WiX
How to make better use of MSI files
The opposite side of it: file preservation and file overwrite rules.

How does Wix decide to install a particular file?

As I put in title, the question is how does Wix decide to install a particular file?
So I have exe file and when I change something in exe file and rebuild it, it will not get reinstalled if I don't change version. But if I change something in resource file, resource file will be replaced even if I don't change version of my application. So how wix is deciding if he need to replace file during upgrade or not.
I am using wix3.9. MajorUpgrade is schedule afterInstallFinalize.
Versioned files get replaced based on file version, yes, but data files get replaced based on whether you have specified file hash or not. I think WiX generates file hases by default, so this is the overwrite rule:
https://msdn.microsoft.com/en-us/library/aa370532(v=vs.85).aspx
and it's a Windows Installer rule that applies to all MSI settup, not a WiX decision.
P.S. afterInstallFinalize isn't an ideal place. afterInstallExecute is safer, and it will have the same overall result. The issue is that after InstallFinalize means that the new product is installed. If the uninstall of the older product then fails and rolls back you will end up with both old and new products installed, otherwise known as a mess. afterInstallExecute makes everything part of the transaction so you get the original product installed if there is a failure to uninstall it.

Downgrade File in MajorUpgrade

EDIT
Please see the small repro at the bottom.
I have the same issue as here: Windows installer deletes versioned file during product upgrade, instead of downgrading it
File Table |File | Component_ | FileName | FileSize | Version|
-----------|--------------------------------------------------------------------------------------------------------------------------------------------
old MSI |fileEcMWtDjRdBXxvVHY.WvW_XXJI4GZcq5iAszC_F3KIwk | Cj9pc73bMjDSVVGUqS81_nPSltSFuUEweshtzct2AHi4 | bftlang.dll | 118784 | 2004.553.4453.1067
new MSI |fileYXlC3cFPRwh6qrJ5u..Ll052XUiMylAmA6a4BwMlz_o | CZJsalkL4nX.r6JS5xXH3pjmr9mY1AO4CITmUEHjP82I | bftlang.dll | 118784 | 2004.553.4453.1064
Component Table |Component | ComponentId | KeyPath
----------------|---------------------------------------------------------------------------------------------------------------------------------------
old MSI |Cj9pc73bMjDSVVGUqS81_nPSltSFuUEweshtzct2AHi4 | {C45097D5-E359-48B5-9F85-AB5EC81D62BF} | filepcu3NI3UMnsXucCthGSqTSHMvUoyVuyQHRbEXnUVii0
new MSI |CZJsalkL4nX.r6JS5xXH3pjmr9mY1AO4CITmUEHjP82I | {8B97BC16-7D4D-45CD-A3E3-903C60868202} | fileYXlC3cFPRwh6qrJ5u..Ll052XUiMylAmA6a4BwMlz_o
MSI (s) (88:A0) [20:12:50:115]: Disallowing installation of component: {8B97BC16-7D4D-45CD-A3E3-903C60868202} since the same component with higher versioned keyfile exists
(off topic: does Stackoverflow markdown support tables? They aren't mentioned in the help.)
For some reason, our incremental build system creates bogus version numbers which sometimes decrease. While I would agree if you tell me to fix that first, it is not under my control and the same issue would manifest itself if someone ever wanted to e.g. downgrade a nuget package.
The workaround mentioned in the linked question (scheduling it before Costing) solves the original problem, but it seems to cause issues with Burn and upgrading.
Another workaround mentioned was changing REINSTALLMODE, but I am using Burn, which afaik doesn't allow me to change it. (We have no shared components, so if it were possible to change the REINSTALLMODE, that would probably be the best solution.)
After scheduling it before Costing, I have a few times observed the issue that the bootstrapper was registered in ARP, but the MSI wasn't installed. (I think this was caused by a cancellation of the upgrade which caused a rollback of the new installation but no reinstallation of the old version - but I am not sure about that).
We generate component GUIDs by calling Guid.NewGuid(), so the component rules are violated, so I can't schedule it after InstallFinalize, but a comment mentions that MSI then keeps the old (higher versioned) version, which is definitely not what I want.
Basically, I have a directory layout in my MSI, and want to copy that 1:1 into the selected directory, ignoring and overwriting any existing files regardless of their version. To solve similar problems with non-versioned files, I use this pattern for each file:
<Component Directory="APPLICATIONFOLDER" Permanent="no" Guid="##Guid.NewGuid()##" Id="##some random id##">
<File Id="##some different random id##" Source="#source#" />
<RemoveFile Id='##some other random id##' On='install' Name='#name#'/>
</Component>
If possible, I would like to schedule RemoveExistingProducts after InstallInitialize, that way the uninstallation is inside the transaction.
Is there a clean way to downgrade a file during a major upgrade? Even though the workaround of scheduling it before costing mostly works, I still had to suppress ICE27, which complained about it.
Edit:
I searched a bit more and found this question which mentions modifying the file table after compilation. I guess that might be a viable option for me, but can it really be that hard to work around an msi bug? (I consider a major upgrade removing a file and not replacing it a bug.)
EDIT2:
I have created a small repro, it contains 6 MSI files:
in with_different_guids, the DLL is downgraded, but both versions have different IDs and different GUIDs.
in with_same_guids, the DLL is also downgraded, but both versions have the same (autogenerated) ID and GUID.
Both show the same behaviour:
If I install 1\SetupProject.msi, the directory contains three files, 1 .txt, 1 .dll and 1.exe.
If I then run 2\SetupProject.msi, only the .txt and .exe get reinstalled (the version of the .exe is unchanged).
In rep_before_costinitialize, the REP is scheduled before CostInitialize, and the MajorUpgrade works, all three files are on disk.
Edit3:
I was also able to reproduce my Burn problem:
If I schedule REP before CostInitialize, the MajorUpgrade works as expected: the DLL is downgraded.
BUT the transactional logic of the Burn Upgrade no longer works as expected.
If I install setupExe\1\BootstrapperProject.exe, then start setupExe\2\BootstrapperProject.exe, but cancel it in the middle, neither MSI is installed, but the Entry for 1 is still shown in ARP.
Repro: https://onedrive.live.com/redir?resid=5062efe7e0c8eccc!124918&authkey=!ANYtHc5SkzFXn5U&ithint=file%2czip
FWIW, I've talked to MS support in the past about having REP before costing to make upgrades work successfully, and at that time they said it was ok, while pointing out that it's also before MigrateExistingFeatures so if you migrate features during upgrades there'll be an issue.
I wouldn't alter the File table. This guarantees that the version on disk does not match the version in the File table, and makes the file a candidate for repair. If the version on disk is 2.0 and the version in the File table is 3.0 then a repair will see that the file is broken. Some major upgrades or patches will notice the difference and demand that you supply the source MSI to restore the file (because of the version mismatch) before deciding if the file needs to be patched or upgraded. Windows cannot know whether a file needs updating by an incoming update if the version on disk doesn't match the File table.
In any case, for an individual file it's much safer and easier to just open the darn file with Visual Studio and change the real version!
There are also occasional bugs like this https://support.microsoft.com/en-us/kb/905238 which cloud the issue.
Do you generate that File table key with your code? I'm suspicious of the fact that your File table key is different in the different MSIs. It is probably not the issue, but changing File table keys has been known to cause issues in other update scenarios. If the upgrade attempts to find the previous version of the file by using the primary key to the file table (which relational databases do) and fails to find it in the older MSI then the results might be unpredictable.

Small updates not applying with WiX

I have WiX building MSIs as part of my automated build. I want the user to be able to install one of these builds and then be able to install the next day's version on top of it.
Doing a minor or major upgrade which would require changing the build number within the ProductVersion, I don't see to be an option for several reasons:
Firstly that the only thing I could think to peg it to is either the subversion revision or the build number but that number overflows at 65,535 which we could hit in the future. Secondly that number is already in use for distributing bug fix releases to customers.
So if I cannot change the ProductVersion then the only option left to me is a Small Update. I tried to achieve this by making sure the package code in the revision number summary had changed by not specifying an id for the package element which according to the documentation will result in a new one being generated each time.
<Package InstallerVersion="405" Compressed="yes" InstallScope="perMachine" />
However when running the MSI this generates an error dialog appears warning the user that this product is already installed. The only way to install it is to pass additional command line arguments to msiexec and even that doesn't seem to actually reinstall the components.
Changing the ProductCode to be automatically generated <Product Id="*"...> but not changing the ProductVersion seems to follows the rules described for setting AllowSameVersionUpgrades to no which is the default value. This means Windows Installer treats it as a separate package and installs it alongside the first one but again does not seem to overwrite the installed files, just adds an entry in the installed programs table.
The definition of a small update or minor upgrade includes the fact that you need to specify REINSTALL and REINSTALLMODE - that's just the definition of how to apply them because as you say, that product code is already installed and you can't reinstall it, only update it with a command line update.
You could package the MSI inside a zipped up exe that extracts the MSI and runs it with the appropriate command line, you may need to write a small program to run the MSI depending on whether the self extracting exe tool lets you run external commands. The only other thing I can think of is that you use one of the ooher fields of ProductVersion to indicate that this is NOT a customer product, (such as minor=254) and that leaves you free to use build number up to 65535, at which point do a major upgrade. In practice that may be a long time.
Changing the ProductCode just makes it a new product and yes therefore it will install side by side unless you have specified a major upgrade.