MSI Major Upgrade overwriting rules - wix

I think I read it somewhere, but couldn't find the source now and can't confirm it: when install(Major upgrade) a newer version from MSI, if the file has been modified (either by installer or user), the default rule is that old file wouldn't be replaced by the same file from a new version?
I think also I observed that behavior in the installer I wrote before, but now after a few changes, seems that it will always replace the old modified config files!
Product definition:
<Product Id="*" Name="$(var.ProductName)" Language="1033" Version="$(var.ProductVersion)" Manufacturer="Advanced Software Solution" UpgradeCode="$(var.UpgradeCode)">
<Package Id="*" InstallerVersion="200" Description="The web service installer" Compressed="yes"
InstallScope="perMachine"/>
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
Component Definition:
<Component Id='WebConfigComp' Guid='GUID'>
<File Id='WebConfigFile' Name='Web.config' Source='$(var.TheWebService.WCF.TargetBinPath)\Web.Distribution.config'
KeyPath='yes'>
</File>
</Component>
InstallExecutesequence
FindRelatedProducts 25
AppSearch 50
LaunchConditions 100
ValidateProductID 700
myScripts_CA 799
CostInitialize 800
FileCost 900
CostFinalize 1000
MigrateFeatureStates 1200
InstallValidate 1400
RemoveExistingProducts 1401
InstallInitialize 1500
BackupCA Installed 1501
ProcessComponents 1600
UnpublishFeatures 1800
SchedSecureObjectsRollback_x64 VersionNT > 400 1801
RemoveFiles 3500
RemoveFolders 3600
CreateFolders 3700
InstallFiles 4000
InstallServices VersionNT 5800
SchedSecureObjects_x64 NOT REMOVE~="ALL" AND VersionNT > 400 5801
ConfigureIIs NOT SKIPCONFIGUREIIS AND VersionNT > 400 5999
RegisterUser 6000
RegisterProduct 6100
PublishFeatures 6300
PublishProduct 6400
InstallFinalize 6600
LunchWCFReadme NOT Installed 6601
Update: I just created a new project for testing, the same behavior observed (the modified file is replaced by the newer version of installer) without change the default InstallExecSequence. Which probably means even though the File Versioning should applies, but it not actually kicked in to affect the result would expected as Remove of old version happened too early be default as Glytzhkof and PhilDW pointed out.
I am using Wix 3.8, the current stable, did I missed something?
Update2:
So far, I can confirm that moving RemoveExistingProducts after InstallFiles will keep the modified unversioned files. But the problem is that seems MajorUpgrade conflict with
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallExecute" />
</InstallExecuteSequence>
I am adding, and the error message is
Error 1 Duplicate symbol
'WixAction:InstallExecuteSequence/RemoveExistingProducts' found. This
typically means that an Id is duplicated. Check to make sure all your
identifiers of a given type (File, Component, Feature) are
unique. C:\TestDev\MySetupTest\MySetupTest\Product.wxs 5 1 MySetupTest
which is not very helpful either.
Final Update:
After digging the web thing for a while, find out what the issue is:
By default, MajorUpgrade schedules RemoveExistingProducts after
InstallValidate. You can change the scheduling using the Schedule
attribute. For example, If you choose to schedule it after
InstallInitialize, it will look like the following:
<MajorUpgrade
Schedule="afterInstallInitialize"
DowngradeErrorMessage="A later version of [ProductName] is already installed. Setup will now exit.">
Source: Wix Toolset website
So including of MajorUpgrade will indeed change RemoveExistingProducts sequence for you, which is a useful feature to have, but was unexpected for me. Thanks for all the help, now things starting make sense to me. An happy ending after all!

When a major upgrade uninstalls an existing installation before the new version gets installed (RemoveExistingProducts before InstallInitialize) it will normally remove all files that were originally installed - this includes files that may have been modified. Then the new version is installed with a fresh bundle of files.
If you schedule RemoveExistingProducts after InstallFinalize the install of the new version's files precedes the removal of obsolete files. In this scenario files are only replaced if they are versioned and newer than installed files, and for unversioned files like txt, pdf, etc... the file replacement rules basically states that the file will only be overwritten if it has not been changed on disk.
It follows that moving RemoveExistingProducts after InstallFinalize may solve your file "replacement problem" which is really a case of the modified files being deleted during uninstalland reinstalled by your current upgrade strategy.

As pointed out, your REP is early in the sequence, so it's essentially an uninstall of all the older product followed by an install of the new one. This is one type of major upgrade.
The other type of major upgrade is when REP is at the "end". In this case the new product is installed on top of the older product, and that will follow file replacement rules, that's the part that's relevant to your question.
This might be useful:
http://msdn.microsoft.com/en-us/library/aa370531(v=vs.85).aspx
and there's different rule if the unversioned files have hash checking. Yes, unversioned means "without a file version in the file's resources".

Behaviour what you are saying is unversioned file replacement logic.
Placing config file in its own component and marking the config file as the key path of the component will ensure that Windows Installer will use unversioned file replacement logic when deciding whether or not to replace this file in the install of your new version.
This post can help you to achieve the same.

I can't comment because of lack of reputation, but if you want to add
<InstallExecuteSequence> <RemoveExistingProducts After="InstallExecute" /> </InstallExecuteSequence>
and if you got duplication error, you should try this instead;
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." Schedule="afterInstallExecute" />

My guess is that the change in behavior is because you have moved RemoveExistingProducts early in the InstallExecuteSequence.
A major upgrade is essentially running an uninstall of the original product, and then a reinstall of the new one. If RemoveExistingProducts is late in the InstallExecuteSequence - and component referencing is done correctly - the existing product is not uninstall first, but its "leftover" components are removed after installing the new product. It works like a "diff" if you like. This will preserve installed files on disk that are not removed in the new version. I hope that was comprehensible - a lot at once, and rushed.
Here is a thread that deals with ensuring unversioned files get updated during an update: Forcing an upgrade of a file that is modified during its initial installation

Related

WIX Installer - is there any automated way of managing Product ID / Code?

I'm developing a Windows Service installer using Wix. From what I understand, I should generate a unique Product ID for every Version of the service. This can be easily done by setting "*" in the Product Id attribute. However, when I do this and I rebuild the installer, it generated a different guid for every build. When I already have any version of service installed on my machine, this causes several problems: i.e. 'exe' file remaining after uninstall process being finished, multiple entires in Programs in Control Panel.
After some investigation, I found that the solution to this would be to update the Product Id manually every time when version number of the service would change. Or maybe I am missing something?
Anyway, the question is: is there any way to make this process automated, so that the guid would be same every time I build the same version of the service but change only when the version is updated? Any external tool for Wix?
Build Automation: You can use build automation in a number of ways to automate the creation of new product GUIDs and similar. You would pass in values to the WiX project file and such things, but perhaps that is not what you want to maintain. Let me add another option below.
Major Upgrade Element: I assume you have a major upgrade element in there? It takes care of uninstalling the previous version if configured correctly. Typically you see multiple versions in Add / Remove Programs when you re-build the same version and install on top of the last installation.
Suggestion: You can manually configure the Upgrade table to uninstall "same version installations". You take out the "auto-magic" of the "MajorUpgrade" element and do things yourself with Upgrade elements (an older way to configure the Upgrade table that yields more detailed control):
Remove this (or just comment it out):
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
Add this:
<!-- The top of your new source - compiler variables, can be overridden at compile time via candle.exe and light.exe switches -->
<?xml version="1.0" encoding="UTF-8"?>
<?define MyProductVersion = "2.0.0.0" ?>
<!-- Sample GUID only, do not use in your package ->
<?define MyUpgradeCode = "88888888-7777-7777-7777-888888888888" ?>
<..>
<Upgrade Id="$(var.MyUpgradeCode)">
<UpgradeVersion Minimum="$(var.MyProductVersion)" OnlyDetect="yes" IncludeMinimum="yes" Property="DOWNGRADE_DETECTED" />
<UpgradeVersion Property="PRODUCTLINE1" IncludeMinimum="yes" IncludeMaximum="yes" Maximum="$(var.MyProductVersion)" Minimum="0.0.0" />
</Upgrade>
<..>
<!-- You have to add RemoveExistingProducts to the InstallExecuteSequence table -->
<InstallExecuteSequence>
<!-- Potential scheduling (after): InstallValidate, InstallInitialize, InstallExecute, InstallExecuteAgain, InstallFinalize -->
<RemoveExistingProducts After="InstallInitialize" />
</InstallExecuteSequence>
Summary: The above should let you rebuild your setup and install any number of
times using the same version number and the installation process will
remove prior installation "auto-magically". Please test. No guarantees. Murphy's law. Etc... :-).
Some Links:
Adding entries to MSI UpgradeTable to remove related products
Doing Major Upgrade in Wix creates 2 entries in Add/Remove Programs
WIX does not uninstall older version (list of common major upgrade problems)

WiX 3.8 keeping config file state during Major Upgrade

I'm using WiX 3.8 (the latest stable release, I think), and I can't seem to get a config file to not get uninstalled-and-reinstalled during a major upgrade.
There are lots of questions about this on SO -- a lot of answers point to this site as a good answer. However, the suggestion given doesn't work (for me).
What the site says is to place each config file in its own component and mark the file as the key path of the component. Something like this:
<Component Id="config.xml"
Guid="*"
Directory="folder_where_config_file_lives">
<File Id="config_file"
Source="$(var.Project.ProjectDir)bin\Release\configFile.xml"
KeyPath="yes"/>
</Component>
Great. Next it says to schedule RemoveExistingProduct after the InstallFiles action, like so:
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallFiles"/>
</InstallExecuteSequence>
Problem is, when I compile, I get this error:
The InstallExecuteSequence table contains an action
'RemoveExistingProducts' that is declared in two different locations.
Please remove one of the actions or set the Overridable='yes'
attribute on one of their elements.
This person also had that problem, but he seems to have solved it. What fixed it for him was adding a scheduling attribute to the , which effectively got rid of the "two different locations" declaration problem (I guess):
<MajorUpgrade Schedule="afterInstallInitialize"
DowngradeErrorMessage="A newer version of [ProductName] is already installed."/>
So when I substitute the schedule change attribute (which contains a attribute itself, I guess), not only does it not work -- the config file gets removed and replaced during the upgrade -- it causes even more weirdness. My project has a bootstrapper with a lot of MSIs, and although I get log files for the installation of all of the MSIs that are after the MSI that contains the config file, they aren't installed.
Let me repeat that: the logs say that the MSIs are installed, but they aren't. There's probably a rollback somewhere that I can't find in the log files, but reading the MSI log files it looks like the installation went swimminly.
Does anyone know a way for a config file to not be removed-and-reinstalled during a Major Upgrade in Wix 3.8? What I've mentioned above is the best info from the interwebs that I could find, but I've tried pretty much everything on SO to no avail.
The MajorUpgrade element has everything you need, including where the RemoveExistingProducts action is scheduled. Don't add a RemoveExistingProducts into a sequence as well.
RemoveExistingProducts shouldn't be after InstallFiles. It's not clear where that comes from, but the documentation doesn't say that's a choice:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa371197(v=vs.85).aspx
When RemoveExistingProducts is sequenced early (such as after InstallInitialize or InstallValidate) it means that you are effectively uninstalling the old product followed by an install of the new product upgrade, and that means uninstalling the config file and installing the one in the upgrade. The way to retain the config file is to schedule REP afterInstallExecute. This results in an upgrade that is basically a version-rules install of the new product over the older installed one. The version rules mean that if you want updated binaries you must update their file versions. The good news about data files (your config file) is that updated data files won't be replaced:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa370531(v=vs.85).aspx
The older product then gets uninstalled, retaining the resulting set of files.
So sequencing of REP afterInstallExecute in the MajorUpgrade seems to be what you want. A caveat is that you need to follow component rules, which should happen automatically if you have auto-generated * guids in WiX.
IMO, Windows Installer was invented before XML caught on and the component rules don't handle it well. What I prefer to do is to not fight this behavior. Write your application so that one config file is owned by the installer and can always be safely overwritten and another config file that holds your user configuration data and that MSI doesn't know about. This second file should take override the first file.

Wix Installer - create installer that always re-installs over tself

I am trying to create an installer that doesn't "carry" any files but does set a registry key and create a small folder structure (if not present)
The script does use CopyFile to copy files from a network location to a folder on the c-drive.
The idea is to create an installer that can be re-run whenever the network files are updated so that they are brought local for the user.
The CopyFile code has a RemoveFile line just ahead of it (in same component) so the file should always be copied "fresh"
The folder structure and registry key don't really need to be repeated but won't matter if they are.
I am also not worried about an uninstall (nor what it does as this install is always needed) but I can't create install after install in the system so I do need to be upgrading somehow.
I have (currently got) these bits of script in my wxs file
http://schemas.microsoft.com/wix/2006/wi'>
<Product
Id='*'
Name='Eclipse Template Installer'
UpgradeCode='$(var.ProductUpgradeCode)'
Version='$(var.ProductVersion)'
Manufacturer='Article 10'
Language='1033'>
<Package InstallerVersion='200' Compressed='yes' Comments='Windows Installer Package' />
<Media Id='1' Cabinet='product.cab' EmbedCab='yes' />
<Upgrade Id="$(var.ProductUpgradeCode)">
<UpgradeVersion Minimum="$(var.ProductVersion)" OnlyDetect="yes" Property="NEWERVERSIONDETECTED"/>
<UpgradeVersion Minimum="0.0.0" Maximum="$(var.ProductVersion)" IncludeMinimum="yes" IncludeMaximum="yes"
Property="OLDERVERSIONBEINGUPGRADED"/>
</Upgrade>
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallInitialize"/>
</InstallExecuteSequence>
When I run the Installer for the first time, it works. Files are copied and all good.
I then tweak one of my copied files to force a new date/time on it and re-run the installer. It pops up indicating it is gathering info but doesn't then replace the file with the older version so I think the installer has concluded it needn't do anything as the current "latest version" is already installed?
If I choose Repair in Programs and Features the file does get updated.
I tried setting the IncludeMaximum to a yes but that doesn't seem to help. Same result.
I have also tried the MajorUpgrade command instead of the InstallExecuteSequence but I get the same end result. Maybe for different reasons but not really sure. Maybe not using it right.
Can anyone tell me how to force my installer to redo everything it originally did even if the version matches?
My client can't make new MSI files so updating the version and rebuilding isn't an option. They just want to update the network files (templates) and rerun the MSI to get the files local.
The first run of the MSI also configures their machine for the templates so it is all very simple, which is what they wanted.
Thanks
Simon
Might be a bad idea (I'm not sure what else this product/tool is supposed to do), but you could condition the MSI standard action "PublishProduct" to '0' so that it never runs. This way, your MSI will install and never register itself on the machine. You can continue to run your installer over and over, and it will never detect that an older version is installed.
This is, of course, not the 'correct' way to go about things, but it works.

WiX MajorUpgrade of Windows Service, preserving .config, and avoiding a reboot

I am struggling to get MajorUpgrade, ServiceControl, .config files to work nicely together. After my other question, I'm now kinda having the opposite problem again.
Before, the files weren't being overwritten because the AssemblyFileVersions were static so I fixed that. 1) Now, even with Schedule="afterInstallExecute" my KeyPath='yes' .config file is still being overwritten even though the existing file modified date is different than the file creation date and it is set as a KeyPath. I'm currently having to overwrite the .config file and restart the service after the install.
2) And even if I fix that, I still have a problem of avoiding a reboot. If I say Schedule="afterInstallInitialize" then I believe the .config file will certainly be removed along with the service too early. If I say Schedule="afterInstallExecute" then the service isn't stopped and after the install a reboot is necessary. (That's right, right?) Stopping the service manually prior to the install let's me avoid the reboot. Adding a net stop custom action could work to replace the ServiceControl I guess, but getting all the conditions right seems complex.
3) As a bonus, I'd like to NOT remove the service at all during an upgrade. Can I just stop the service, replace the binary, and start the service again? That will avoid re-entering the service account credentials for an upgrade. But of course it still needs to install on a first install and uninstall on a feature removal.
Here's the meat of it (which is also bundled later, in case that somehow matters):
<MajorUpgrade DowngradeErrorMessage="A newer version is already installed."
Schedule="afterInstallExecute" />
<ComponentGroup Id="ServiceCG">
<Component Id="Service" Guid='*' Win64='yes' Directory='INSTALLDIR'>
<File Id='ServiceEXE' Source='$(var.root)Service.exe' />
<ServiceInstall Id="ServiceInstall"
Name="MyService"
DisplayName="My Server"
Type="ownProcess"
Start="auto"
ErrorControl="normal"
Description="My Server Service"
Interactive="no"
Account="[...]"
Password="[...]" />
<ServiceControl Id="StopService" Name="MyService" Start="install"
Stop="uninstall" Wait="yes" Remove="both" />
<util:User Id="UpdateServiceAccountLogonAsService" UpdateIfExists="yes"
CreateUser="no" Name="[SERVICEACCOUNTFULL]"
LogonAsService="yes"/>
</Component>
<Component Id="ServiceConfig" Guid='*' Win64='yes' Directory='INSTALLDIR'>
<File Id='FileServiceConfig' KeyPath='yes'
Source='$(var.root)Service.exe.config' />
</Component>
</ComponentGroup>
Related but unanswered:
Prevent service removal/install during WiX major upgrade - service not stopping
WiX version 3.8.1128.0
EDIT: it seems this explanation of the same issue, or on the same topic at least, might be easier to understand: Msiexec: automatic rollback to previous version on installation failure
You are banging your head against several core MSI usage problems here.
File versioning: during installation the default file overwrite mode (defined by the REINSTALLMODE property) won't replace files that are equal version by default. This can be changed by setting REINSTALLMODE = "emus". This will replace files with equal version for versioned files. Unversioned files will be preserved if modify and create dates are different.
Upgrade behavior: like Chris says, files that appear to be reverted to default are actually uninstalled and reinstalled due to major upgrade configuration. File preservation is only possible in major upgrades if RemoveExistingProducts is placed late in the InstallExecuteSequence. Then shared files between releases are never uninstalled and the file versioning rules described in point 1 apply for overwriting.
Service configuration preservation: avoiding re-entry of service credential information is a common problem for major upgrades that uninstall early in the InstallExecuteSequence. In other words the product is uninstalled and then reinstalled wiping out changed files. I don't recommend it, but some people argue for this solution: How to only stop and not uninstall windows services when major upgrade in wix? (Rob Mensching is the WIX and Orca author - I think he is suggesting this solution as an option, and not necessarily a recommendation. Please ask in the linked post to be sure). With correct component referencing and uninstall placed late in the InstallExecuteSequence this problem is normally avoided altogether, and this is then a preferred approach (normal component referencing prevent the component from uninstalling altogether leaving service settings and changed config files intact - if, and only if, component referencing is correct - see description of this concept below). However, my preferred approach is still to use a separate MSI for the service install and configuration if you are using a user account to run the service - then it is a self contained deployment unit and can be included in a bootstrapper and updated on its own, and best of all: it is undisturbed by any other application changes or hotfixes. Finally I would like to point out that a service running with a user account isn't a recommended approach to begin with - for security and deployment reasons alike.
Component referencing: refers to the GUIDs assigned to MSI components and how they must match one, and only one (absolute) path at all times through all upgrades. See a better discussion of this with a couple of examples here: Change my component GUID in wix?
I didn't mention the option of setting the MSI components installing services to permanent to prevent their removal on uninstall for the simple reason that this isn't good practice at all. Then files and registration will remain on final uninstall, and you need a custom action to clean up. Very bad practice and bound to cause lots of extra work and problems with dangling component references.
The file create/mod rule only applies to install/reinstall a component. It doesn't prevent the component from being uninstalled. Your major upgrade is scheduled very early which means the previous version is completely uninstalled and then then the new version is installed. This is why your file is overwriting when you don't expect it to. Schedule RemoveExistingProducts later to avoid this problem.
Set your Stop attribute to both install and uninstall. Those two changes should solve your problems.

Wix MajorUpgrade problems

Here're the scenarios.
1.) Install 1.0.1.1 then upgrade to 1.0.2.2 ===> This works well (as expected)
2.) Install 1.0.2.2 then downgrade to 1.0.1.1 ===> This doesn't work (as expected)
3.) Install 1.0.1.1 then install 1.0.1.1 ===> This goes into Repair/Remove mode (as expected)
4.) Install 1.0.1.1 then install 1.0.1.2 ===> It is installed like a new one and show the program twice in Add/Remove program in control panel (I suppose it should work like 3.) because, from my understanding, MSI doesn't care the 4th part of product version)
Am I doing anything wrong ?
<Product Id="*" Name="My product" Version="$(var.Version)" Language="1033" Manufacturer="My Company" UpgradeCode="MY-UPGRADE-CODE">
<Package Description="My product" Comments="Comments" InstallerVersion="200" Compressed="yes" />
<MajorUpgrade Schedule="afterInstallInitialize" DowngradeErrorMessage="A later version of [ProductName] is already installed. Setup will now exit." />
This is behaving as mentioned in the WIX documentation. You need to set the below property to "Yes" in the MajorUpgrade element to handle this scenario. Read the highlighted text for more details.
AllowSameVersionUpgrades (YesNoType)
When set to no (the default), installing a product with the same version and upgrade code (but different product code) is allowed and treated by MSI as two products. When set to yes, WiX sets the msidbUpgradeAttributesVersionMaxInclusive attribute, which tells MSI to treat a product with the same version as a major upgrade.
This is useful when two product versions differ only in the fourth version field. MSI specifically ignores that field when comparing product versions, so two products that differ only in the fourth version field are the same product and need this attribute set to yes to be detected.
Note that because MSI ignores the fourth product version field, setting this attribute to yes also allows downgrades when the first three product version fields are identical. For example, product version 1.0.0.1 will "upgrade" 1.0.0.2998 because they're seen as the same version (1.0.0). That could reintroduce serious bugs so the safest choice is to change the first three version fields and omit this attribute to get the default of no.
This attribute cannot be "yes" when AllowDowngrades is also "yes" -- AllowDowngrades already allows two products with the same version number to upgrade each other.
Source: WIX Documentation