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.
Related
We have 2 installer sources in WiX to create installer for a single product with same Product Version, GUID and Package GUID also.
Those 2 installer projects will yield different outputs, one output being just a single MSI file (File1.msi) and other project output is a CD-ROM structure having different MSI file name (File2.msi).
So now issue arises when we installed the product using single MSI file, upon that if we invoke MSI from the other CD-ROM output, we end up getting below mentioned error.
I tried keeping same MSI filename for both kind of installer output, then this above error dialog was resolved but repair functionality isn't working.
If some files were deleted in the product's destination folder, it says source file not found error pointing to CD-ROM installer source folder.
Please help where I'm going wrong. I want to support Repair installation without this errors.
The dialog is expected. You can't change the name of the MSI except during major upgrades.
After that, if you rebuilt to create the different layouts, each MSI probably has a unique PackageCode and that makes them unique packages. That is most likely why repair isn't working. A verbose log file should tell all.
Updated: Compile your main MSI, then run administrative image on it and put the extracted files and MSI on the CD? Put the compressed
version on there as well - just in case they prefer that kind of
release (happens).
I am not sure what will happen when you run both setups this way, but
I think the MSI flagged as an administrative image extract might be
detected by the engine. I am not sure. Should work. Built-in approach for MSI, and you are not fighting wind-mills.
User Accounts: Are you creating any NT User Accounts? Did you set the FailIfExists attribute to yes? Please check here:
User Element (Util Extension). What is the setting for UpdateIfExists? (if any).
Other Issues: There might be other issues as well as Rob mentions. You can not use the same package code for both release types because a package code by definition identifies a unique file. All kinds of X-Files-like problems occur if you try to "hack" this. Not a fight you want to take on.
Administrative Installation: Why would you want to distribute different setups on CDs these days? Corporations that use your setup will run an administrative installation on your setup extracting all files - which is a much better concept. It is essentially a glorified file-extraction, and it is a built in Windows Installer concept intended to make a network installation point for software - among other things. It essentially extracts all files and translates the Media table to use external source files.
List of Links:
What is the purpose of administrative installation initiated using msiexec /a?
Extract MSI from EXE
I have an installer that contains a uncompressed file:
<Component Id="Uncompressed">
<File Id="UncompressedFile" Vital="yes" Source="Uncompressed.dll" Compressed="no" />
</Component>
This file can be replaced before actually installing the Package. That means, the version number at build time differs from the version number at install time. The MSI always contains the version at build time though. As a result the file does not get updated.
What possibilities do I have to:
Check the file version at install time (ideal)
ignore the file version for this file (if 1. is not possible)
Can I create the installer without actually supplying the file at build time?
In this related question there are some possible workarounds:
using REINSTALLMODE=amus
=> I would prefer a solution that only affects this single file.
Rename file on install (and undo on rollback)
=> Might work, seems hacky though
Companion File
=> Seems to be the best solution right now
Version lying
=> Won't work, since the File is a DLL that actually contains a version
This article seems to be exactly what I want
=> I'm just not sure how to use it.
Your process is less than optimal. If the MSI's file table contains a version that is not the same as the file that you actually install, then there is potential for several issues. If the file version on disk never matches the version in the MSI then there is scope for repair issues, trying to correct the broken install. When there is a major upgrade (or a patch) Windows will see the versions mismatch and will not know if the file on disk should be replaced with the new one in the upgrade because it can't do a proper version comparison.
I would not attempt to perpetuate this situation. The easiest fix is to run msifiler.exe (from the Windows SDK) to fix the MSI File table versions when the file is replaced.
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.
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.
I'm learning about Windows-Installer and Wix, and have a number of questions related to how it works:
If a component GUID changes, but the same files are in the component, what happens on a major upgrade? Do the files get replaced?
If a component is removed from a product, what happens to the associated files on a major upgrade? Do the original files get removed on an uninstall?
Am I correct in saying that a major upgrade will replace all files in all components, regardless of whether the assembly version of the file has changed, and that on small updates and minor upgrades, it only replaces a file if the GUID is the same, and the assembly version of the file has been incremented? What if the file doesn't have an assembly version, like an aspx page?
Suppose that a product was deployed on a machine without using an installer. If you then created an installer, with files of the same name in a component as what's in the installed directory, what happens to those files if you tried an installation? Are they replaced?
Am I correct in saying that if I used a tool like heat to create an xml file with all the files in a directory (like for a website), that you'd have to keep the GUIDs the same (manually or with a script), or you'd only be able to do major upgrades?
If a component GUID changes, but the
same files are in the component,
what happens on a major upgrade?
First, the question is whether the old component gets uninstalled. If you don't configure your upgrade to uninstall previous versions of your product, then the component will not be removed (although its files may be overwritten). See also answer to question 2.
Second, the question is whether the new component will be installed. A component is only installed if its keypath is missing. If the keypath is a versioned file, then a lower version also counts as "missing".
Finally, if the new component was marked for installation, and Windows Installer encounters a file with the same name as the one it is trying to install, the File Versioning Rules determine whether the file is replaced or not. For example, a file with a higher version will not be downgraded.
If a component is removed from a
product, what happens to the
associated files on a major upgrade?
Do the original files get removed on
an uninstall?
Unless you put the right entries in the Upgrade table and the InstallExecuteSequence that tells windows installer to remove the old product, the old components will be left alone. See this blog post by Alex Shevchuk for guidance on how to create an installer in wix that removes old versions.
Am I correct in saying that a major
upgrade will replace all files in all
components ...
No. It depends on whether the old component was removed first, whether the new component was installed depending on the keypath, and the file versioning rules.
Suppose that a product was deployed on
a machine without using an installer.
If you then created an installer, with
files of the same name in a component
as what's in the installed directory,
what happens to those files if you
tried an installation?
Again, it depends on whether the components get installed, depending on their keypath, and the file versioning rules.
Am I correct in saying that if I used
a tool like heat to create an xml file
with all the files in a directory
(like for a website), that you'd have
to keep the GUIDs the same (manually
or with a script), or you'd only be
able to do major upgrades?
Correct. The GUID is the identity of a component, so if you would change the GUID there would exist two components (in the old and new version of your product) that installed the same resources to the same target location. And that's a no-no according to MSDN: "Never create two components that install a resource under the same name and target location."
Learn the Component Rules. They're very easy to break and Windows Installer doesn't enforce them. However if you don't follow the rules, then weird strange voodoo happens.
Easy solution, stick with one file per component and use heat with compile time GUID generation (outputs with Guid="*" uses a stable algorithm, it's not random). Having heat generate GUIDs is random, but GUIDs generated by candle at compile time will be stable (based on filename + path hash or something from memory)
If windows installer finds a file already on disk during install, it will increment the reference count for that file assuming it's a "shared" file. Files are only removed from disk once the reference count returns to zero so if a file already existed, the count may never return to zero and you can get files left lying around even after uninstalling.