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.
Related
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.
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.
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 working on the upgrade feature for my WiX-based installer.
As part of the instalation, we are installing a web.config file and then using a custom action to update the connection strings inside the file.
But this causes a problem when we run our upgrade. We would like to have the RemoveExistingProducts scheduled for after InstallFinalize since this is most efficient in terms of not removing and reinstalling files that have not changed. But this leaves the original web.config file in place at the time when Windows Installer is trying to determine whether it should update it or not. Since it's last modified date is more recent than its creation date, Windows Installer decides not to update it (see versioning rules that Windows Installer uses). But we need it to be updated.
One obvious solution is to change the scheduling of RemoveExistingProducts to after InstallValidate - but this is inefficient, and also, I don't think it would give us the opportunity to migrate settings from existing files, should we need to do that.
Any other ideas?
Newer answers: 1) Companion files, 2) file version hack using Visual Studio, 3) moving the file to another installation path, 4) variations of REINSTALLMODE, 5) "version lying", etc... All kind of options, most of which are not ideal:
File of a new component isn't installed because there was an old component with the same file
How to Explicitly Remove dll During Majorupgrade Using Wix Toolset
Below is an older answer. I don't think option 2 works properly anymore:
There are many ways - none are ideal.
1: You can use a companion file to force update of the file in question. Provided the companion file specified always gets updated, this may be the way to go. Essentially this means that you link the non-versioned file to the version update logic of its companion file (files are updated together). I have never used this in WIX, but I think it's as easy as adding the CompanionFile attribute to a File element and point to the ID of the file you want to "version follow". Inside the MSI file it will look something like this:
2: You can use a custom action to delete the file before file costing (or better yet, rename it to a backup format). The problem is that if the setup fails the file will be missing. If you rename the file instead of deleting you can put it back in case the setup fails via a rollback custom action. Sometimes I use the RemoveFile table to remove files on install, but depending on the sequencing specified in InstallExecuteSequence this may not work (deletion must happen before msi does file costing).
3: Then there is the sledgehammer approach: set REINSTALLMODE = amus to force overwrite all files regardless of version. I shouldn't even mention this since it is horribly dangerous (you can end up overwriting system files, or on newer Windows versions trigger a nasty runtime error as files are protected). Use it only for dev testing, and don't think it is a quick fix. It causes more problems than it solves.
As a variation, an acceptable approach may be to set the REINSTALLMODE to emus (replace older and same version files). This can help if you don't want to increment the version numbers but keep rebuilding your binaries - as is the case in a lot of .NET. My guess is this will cause a whole new range of problems though - most significantly binary different but version identical files in the wild if you use it for public releases - a deployment smell if ever there was one. As a QA/DEV only approach it could work though. But seriously, why bother? Just auto-increment the build version of the binaries and the problem is solved reliably.
Links:
How to Explicitly Remove dll During Majorupgrade Using Wix Toolset
Only iffy ones. You could remove the specific file early with a custom action, but be sure to condition this right! Or you could specify a version for the file so upgrade rules will treat it like replacing a non-versioned file with a versioned one, but then patches can get antsy about having the wrong version of this file.
Don't use a custom action to update your config file is the other obvious idea. Instead get WIX to do the update via the XML extensions. E.g.
<Component Id="web.config" Guid="f12ff575-ad5f-47bc-a5c9-40b1e3a7f9f5" >
<File Source="$(var.SrcPath)\web.config.config" KeyPath="yes" />
<util:XmlConfig Id="AppSqlInstanceName"
File="[#web.config]"
Action="create"
ElementPath="//configuration/connectionStrings/add[\[]#name='YourStringKey'[\]]"
Name="connectionString"
Node="value"
Value="metadata=res://*/YourModel.csdl|res://*/YourModel.ssdl|res://*/YourModel.msl;provider=System.Data.SqlClient;provider connection string="data source=[SQLSERVERANDINSTANCE];initial catalog=DatabaseName;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework""
On="install"/>
</Component>
This is using a [SQLSERVERANDINSTANCE] variable which needs to be setup before hand.