WiX HowTo: Downgrade a third-party dependency without re-installing? - wix

Background
WiX & the Windows Installer are completely new to me.
In production, we used an MSI (created using WiX) to install our software. The MSI references a third-party assembly (e.g. OtherCompany.ThirdParty.dll, version 2.5).
The next release of our software must reference an older version of the third-party assembly (e.g. OtherCompany.ThirdParty.dll, version 1.7).
While I understand that installing an older version of a dependency is uncommon, it must happen.
So my question is... how do you configure a MSI (generated by WiX) to use an older version of an assembly without having to completely uninstall the existing package?
Options
We have explored the following:
Increment the assembly's version
it's a third party assembly, and
for traceability this is not an option
rename the assembly
the dependency is being retrieved using NuGet... so this won't be straight forward
force existing install to be completely removed (automatically or manually)
we don't want configuration information that was collected during the previous installation to be lost, so this isn't an option
schedule RemoveExistingProducts before costing
not recommended by Microsoft (see: MSDN)
custom action: to delete dependency
if the installation fails, the application may be left in an undefined state
override file version in setup
moving forward, this will be error prone
changing the REINSTALLMODE
From the articles that I have read, it appears that this should only be used as a last resort.
use a WIX companion file
am still investigating
For Moderators
I am aware that there are other SO posts on this subject. Please note that several of the recommended solutions are incomplete or are error prone.
References
MSDN: Patching and Upgrades
MSDN: RemoveExistingProducts Action
downgrade a library during a msi upgrade
Why Windows Installer removes files during a major upgrade if they go backwards in version numbers
MSI Writing Guidelines
What Every Developer Should Know About MSI Components
Windows installer deletes versioned file during product upgrade, instead of downgrading it
MSDN: Windows Installer - File Versioning Rules
Msiexec REINSTALL=ALL REINSTALLMODE=vamus not reinstalling anything
good overview of what is happening under-the-hood
Forcing an upgrade of a file that is modified during its initial installation
this is an older post is from 2009

Some issues are best solved by the application design rather the deployment.
There are two places to save a particular version of a .NET assembly: the GAC or the application folder (or subfolder with probing privatePath). In either case, you might want to use a bindingRedirect.
Also, you can load from a specific location using AppDomain.AssemblyResolve, provided the binding is not successful using the GAC.
General Reference: How the Runtime Locates Assemblies—thanks to #Pressacco.

Related

How to express a file dependency using WiX

I have a couple of MSI files installing different applications. All these packages share the same underlying runtime which basically is a set of DLLs. This runtime is pulled in each of the installers as a merge module. Installing a couple of these packages works just fine, always the latest version of the runtime stays on the system and when the last package is removed everything is removed from the system.
Now I had to split one of the DLLs into 2 and added a new component to the runtime installing the new DLL. This new DLL is linked with other libs of the runtime. Now assume the following scenario:
install an old package with the merge module for the runtime without the new DLL
install a new different package with a newer version of the merge module for the runtime. Now there are 2 packages on the system
remove the new package again
Now the old package is broken because:
the new component for the new DLL had a reference count of one as the old package didn't have it and therefore gets removed
the other runtime DLLs stay on the system because they are still referenced by the older package. However as they are new they are already linked with the new DLL that is now no longer present
So my question is:
Is there either a way to explicitly state in the WiX code that file A depends on file B so that it stays on the system until all references have been uninstalled?
or is there a way to explicitly downgrade the dependees in a way the dependency does not longer exists?
Am I doing something fundamentally wrong?
What I did try on a clean machine was to follow Stein Åsmul suggestion like this:
<Component Id='OldLibsNowDependingOnNewLib' Guid='C8DCD2AB-CBE5-4853-9B25-9D6FE1F678DD'>
<File Id='LibOne' Name='LibOne.dll' Source='$(var.SourceDir)/LibOne.dll' />
<File Id='LibTwo' Name='LibTwo.dll' Source='$(var.SourceDir)/LibTwo.dll' />
</Component>
<Component Id='NewLibComponent' Guid='CD2DB93D-1952-4788-A537-CE5FFDE5F0C8' Shared='yes'>
<File Id='LibNew' Name='LibNew.dll' Source='$(var.SourceDir)/LibNew.dll' />
</Component>
However unfortunately this doesn't change the behaviour.
UPDATE: Looking in the SDK again, I see the flag msidbComponentAttributesShared for components. This
looks promising for the problem you describe. Please try to enable
this flag and recompile the version 2 of your setup (unless it is
live).
Enable the Shared flag for the component in question (last part):
<Component Feature="Product" Shared="yes">
This seems to be for patch support, but maybe it will work for your case too. From the MSI SDK:
"If a component is marked with this attribute value in at least one package installed on the system, the installer treats the component as marked in all packages. If a package that shares the marked component is uninstalled, Windows Installer 4.5 can continue to share the highest version of the component on the system, even if that highest version was installed by the package that is being uninstalled."
I think the above should work, but don't have time to test right now. Leaving the below for review.
Short Answer: Use WiX's Burn (setup chainer) to install in sequence the application setup and a new, separate runtime setup that can be handled
independently of your application setup versions.
Prerequisite Setup: Interesting case. This is why I like to split prerequisites into its own MSI package and deploy it via a Burn Bundle Bootstrapper. Burn is WiX's bootstrapper / downloader / chainer - essentially a way to run several setups in sequence - in a few different formats such as MSI, EXE, MSU, MSP. When doing this - putting the runtime in its own MSI - there are no entanglements and you get good decoupling of your runtime and application-specific files. In other words: you can update the runtime files on its own - with their own MSI. The files will even have a reference count of 1 meaning you can easily uninstall them all (not if you install via a merge module that also can be included in other packages - more below).
Merge Modules - Semi-Static Linking?: In a weird way merge modules are sort of semi-static linking. The whole merge module is a version - a binary bundle (think COM) - but its installation behavior is one of "higher version wins" only. Hence a single newer MSI with the newest merge module in it will update the shared files for all applications that use them. Uninstalling will then do what you see: preserve the files that were originally installed by older setups.
Options: One "solution" in your case could be to re-compile the older setup with the newer merge module and then reistall, which I understand you don't like. I don't like it either. I guess it is no solution at all. Some other suggestions:
Permanent component: You can set the hosting component for the new file to be permanent on the system. This is very easy, but also quite silly and not always very desirable. Uninstall will then not remove the file at all.
Prerequisite: This is my favorite option mentioned above. You compile a prerequisite MSI setup that installs the runtime components. This MSI can deliver updates to itself without affecting the main application. This is the primary benefit I am after: Cohesion & Coupling benefits.
Merge Modules: I would avoid merge modules altogether, but it is common to merge the same merge module into the prerequisite setup - if you have a merge module already.
In most cases merging a merge module is fine since you then install the prerequisite and then you can install and uninstall application versions at will without affecting the runtime since a different product (prerequisite MSI) installed the runtime - and that setup should stay behind and not be uninstalled.
If the merge module does not work and brings along the conflict that you already had, maybe try to combine with the msidbComponentAttributesShared "solution" mentioned above. Not tested by me so far. Always risky to suggest things like this, but it is "best effort".
WiX Include Files: I prefer to use WiX include files which allows me to pull in new files without re-authoring a whole merge module in binary format (think C++ include files as opposed to a merge module's COM-style binary reuse).
Side-By-Side: Many people prefer to install prerequisites side-by-side so that several versions of the runtime can co-exist. This may or may not involve the GAC. Switching runtime versions would then be a manifest-manipulation task. Generally somewhat confusing, but doable. You can use both merge modules and separate MSI files to deploy such runtimes - as described above. I would definitely use a prerequisite MSI.
I can't think of more right now, but I know I have forgotten something important this time. Let me persist what I have for now in case it sparks ideas for you.
Cumbersome Prerequisite Setups: Note that prerequisite MSI files are not so bad for corporate deployment since deployment systems will allow one to define relationships between MSI files and to set up deployment chains. For home users you can easily wrap everything in a large setup.exe.
Nonsense Options: Options that don't make sense would be to roll the new file into both setup versions. No gain, lots of overhead. Some people like to copy new files locally to the main installation folder. Does not work since the files it is linked to are likely elsewhere (runtime location). Static linking wouldn't be relevant in this case I think. Only as a last resort to solve a live problem I guess. Setting the SharedDllRefCounter flag will not affect MSI reference counting, it is for legacy reference counting (non-MSI setups), though tweaking this manually is an emergency "solution". The last resort people end up with is typically to abandon the runtime installation and install everything to the same installation folder. Then you have to always recompile everything for every release - which is what you want to avoid?
Some Links:
WiX (Windows Installer Xml), Create universal variables
Pre-Processor constructs, features, Burn Bundles and beyond

"Downgraded" MS dll disappears on upgrade - Windows Installer

We have developed an application that is distributed through Windows Installer, created with the use of WiX, where our customers can upgrade from any older version to the newest.
Our latest version however, deletes 2 dll's, and this is only rectified through a reinstall.
Details on the NuGet packages
Microsoft.IdentityModel.Protocol.Extensions was upgraded from Nuget Version 1.0.2.206221351 and File version 1.0.20622.1351 to Nuget version 1.0.4.403061554 and File version 1.0.4.54.
Similar changes happened to file versioning of System.IdentityModel.Tokens.Jwt from Nuget version 4.0.2.206221351 to Nuget version 4.0.4.403061554.
So by changing how the File version was calculated, MS effectively changed the version to a downgrade of the previous (from 20622 to 4 on the build version).
Why the dll's are removed on upgrade
Some call it a bug, and some call it a feature, but what happens, is that the MSI has a step where it records all the files that needs to be upgraded, it then uninstalls the current version, and then only installs the files that was unchanged or bumped in version - any downgrades are left out.
Question: How do we get around it?
We are shipping this product to a lot of different customers, with very varied technical skills, so an upgrade better work, or we will be flooded with support issues. Are there anything I can change, without actually disabling features like the ability for the MSI to rollback in case of errors, which I have seen as a trade off for others solving the same issue.
You have a few options. One is to change where you schedule RemoveExistingProducts. Another is to use REINSTALLMODE=AMUS instead of the default OMUS. Another is to do version lying on the offending DLLs so that they always get reinstalled. (Author the file element so the version is 65535.0.0.0 or something like that.)
It's an MSI feature... the bug is in Nuget releasing a newer DLL with a lower version #. That breaks MSI's component rules and default file versioning rules.
This might help:
https://blogs.msdn.microsoft.com/astebner/2015/11/16/why-windows-installer-removes-files-during-a-major-upgrade-if-they-go-backwards-in-version-numbers/
and basically Chris is correct, and:
The RemoveExistingProducts placement should be before the costing actions which cause the error.
Another alternative is to open the files with Visual Studio and alter the file versions in the resources so that the rules are followed. File version would not affect assembly version. Just run Visual Studio, then File-Open File, and the Resources section will show where you can change the version.
It's also very likely that a repair will restore the missing files because the missing files "break" the product install. That's easy to test. If that is really the case, then it will repair automatically (and just once) if the app starts with a shortcut, or you can arrange for the shortcut to be advertised. If not, I've seen people use MsiProvideComponent () to make sure the files exist because that will do the repair/install.

Wix:Disallowing installation of component: since the same component with higher versioned keyfile exists

I am doing a compatibility check of MSI in the OPS environment before deploying to Production environment.
A part of this i am deploying first the latest MSI say "MSIv2 to OPS environment on top of MSIv1 and the MSIv2 automatically uninstall the MSIv1 and install MSIv2 without any issues.
Now when i am installing MSIv1 on top of MSIv2. MSIv1 is installed and it is showing in control pannel as installed.But when i see the directory path no .dll files are there in the bin folder.
i am logging the action of MSI in log file which tell ...
Log:
Disallowing installation of component: {AC7BC9EB-4F1D-4FEE-B0C2-478966229D8E} since the same component with higher versioned keyfile exists
Forgive me for the long answer, but I think its important that you understand the basics:
Did you change the product code between the 2 MSI versions? My guess is that you haven't changed the product code between the 2 versions and windows installer is considering
this as a minor upgrade. First understand the difference between Major vs Minor upgrade.
Minor Upgrade
A minor upgrade is an update that makes changes to many resources. None of the changes can require changing the ProductCode. An update requires a major upgrade to change the
ProductCode. A minor upgrade can be used to add new features and components but cannot reorganize the feature-component tree. Minor upgrades provide product differentiation
without actually defining a different product. A typical minor upgrade includes all fixes in previous small updates combined into a patch. A minor upgrade is also commonly
referred to as a service pack (SP) update.
Major Upgrade
A major upgrade is a comprehensive update of a product that needs a change of the ProductCode Property. A typical major upgrade removes a previous version of an application
and installs a new version. A major upgrade can reorganize the feature component tree. During a major upgrade using Windows Installer, the installer searches the user's
computer for applications that are related to the pending upgrade, and when it detects one, it retrieves the version of the installed application from the system registry.
The installer then uses information in the upgrade database to determine whether to upgrade the installed application.
Now during Minor upgrade windows installer uses the following rules to replace existing files - Because unnecessary file copying slows an installation, the Windows Installer determines whether the component's key file is already installed before attempting to
install the files of any component. If the installer finds a file with the same name as the component's key file installed in the target location, it compares the version, date,
and language of the two key files and uses file versioning rules to determine whether to install the component provided by the package. If the installer determines it needs to
replace the component base upon the key file, then it uses the file versioning rules on each installed file to determine whether to replace the file.
At the core of any installer is the actual installation of files. Determining whether to install a file is a complex process. At the highest level, this determination depends on
whether the component to which a file belongs is marked for installation. Once determined that a file should be copied, the process is complicated if another file with the same
name exists in the target folder. In such situations, making the determination requires a set of rules involving the following properties:
Version
Date
Language
The installer only uses these rules when trying to install a file to a location that already contains a file with the same name. In this case, the Windows Installer uses
the following rules, all other things being equal, to determine whether to install.
Highest Version Wins—All other things being equal, the file with the highest version wins, even if the file on the computer has the highest version.
Versioned Files Win—A versioned file gets installed over a nonversioned file.
Favor Product Language—If the file being installed has a different language than the file on the computer, favor the file with the language that matches the product being
installed. Language-neutral files are treated as just another language so the product being installed is favored again.
Mismatched Multiple Languages—After factoring out any common languages between the file being installed and the file on the computer, any remaining languages are favored according
to what is needed by the product being installed.
Preserve Superset Languages—Preserve the file that supports multiple languages regardless of whether it is already on the computer or is being installed.
Nonversioned Files are User Data—If the Modified date is later than the Create date for the file on the computer, do not install the file because user customizations would be
deleted. If the Modified and Create dates are the same, install the file. If the Create date is later than the Modified date, the file is considered unmodified, install the file.
During a Minor Upgrade, the default file versioning rules can be overridden or modified by using the REINSTALLMODE property. The installer uses the file versioning rules specified by the
REINSTALLMODE property when installing, reinstalling, or repairing a file. The default value of the REINSTALLMODE property is "omus".
Now you have to decide whether you are going to do a MinorUpgrade or a Major Upgrade for your MSI. If it is a Major Upgrade, then by default the old version of the product is uninstalled
before installing the new version. Use the link "WIX MAJOR UPGRADE" below for more details on how to implement this. You can also set the below property
within the MajorUpgrade Element - to make sure that you can install an old version on top of the new version.
AllowDowngrades YesNoType When set to no (the default), products with lower version numbers are blocked from installing when a product with a higher version is installed;
the DowngradeErrorMessage attribute must also be specified. When set to yes, any version can be installed over any other version.
If you are sticking to a minor upgrade then, you will need to override the default file versioning rules using the REINSTALLMODE property as mentioned above and use the code "d"
d Reinstall if the file is missing or a different version is present.
Then use the following msiexec command:
msiexec.exe /i installer.msi REINSTALL=ALL REINSTALLMODE=vdmus
MINOR UPGRADE
MAJOR UPGRADE
MINOR UPGRADE - Replace Existing Rules
MINOR UPGRADE - File Versioning Rules
REINSTALLMODE
WIX MAJOR UPGRADE
Hope that helps!
The product code is "*" in wix.
MSI get the version from one of the exe, which is deployed as part of msi. The exe is install the service when MSI is installed.exe is versionsed so MSI will have the same version.
So when the lower version of MSI is installed the exe will also have the same version, hence exe compares with exe which is already there in the server and it decides not to install as the higher version already there.
So what I did is "RemoveExistingproduct" before="costing "
Which resolved the issue removing the product before the higher version.
Let me know if any other alternative is there and is it a right practice and what are the consequences.

Wix major upgrade, replace files regardless of newer file version

My WiX installer (Wix 3.10, MSI 4.5) uses MajorUpgrade for updating. The files to be installed are harvested with heat.exe in pre-build. The current (older) msi file contains a file nlog.dll (which came with a NuGet package v4.1.0) that has a file version of 4.1.0.0, a product version of 4.1.0 and last write time of 2015-09-01.
Since the nlog team ran into some strong naming issues, they published an updated NuGet package v4.1.1, containing an updated nlog.dll with its file version decreased back to 4.0.0.0 while its product version has been increased to 4.1.1, last write time is 2015-09-14.
Now I'm running into a related issue as Robbie did here: wix major upgrade not installing all files: When I install the new msi package and the major upgrade is performed, the present nlog.dll (which is newer according to its file version, but older according to its file date and product version) is being removed, but the new nlog.dll isn't installed.
However, using Schedule="afterInstallExecute" or Schedule="afterInstallFinalize" as suggested won't do the trick for me. Instead of removing the newer file and not installing the older one as in Robbie's case, it doesn't overwrite the present file, and just leaves it in place.
Long story short, I would like my installer to simply install all files that come with it, regardless of any file/product/assembly versioning stuff. There are valid circumstances in which replacing a newer file with an older one is desired. Can't you just tell the installer engine to ignore file versions/dates? If not, what are my options?
You can set the REINSTALLMODE property to AMUS instead of OMUS. This will affect all components globally.
The other trick is to use "version lying". This is where you author the file element with a higher version. Using heat can make this difficult as now you have to transform the XML before compiling it.
Of course the real solution is to hit the nlog team over the head. But based on what I've seen from them over the years it'll never happen. Perhaps you just use a resource editor to hack the DLL and 'fix' the version #. That's assuming you don't need it strong named. This feels dirty and a possible CM nightmare to me though.
Or just dump nlog. :)
If this is a major upgrade and you want everything to be uninstalled before the new product is installed, then you schedule RemoveExistingProducts after InstallInitialize or InstallValidate. That does the uninstall first.
I can't tell if you're getting the "disallowing install..." issue or not, but if you are, and there are other clients of the Dll (it's shared with other installed products) then I'd see if that Dll has support for private copies so you can have you own private copy for your product. If it is shared with other products I wouldn't use version lying - I'd open the Dll with Visual Studio "open as file" and change the version! Make that your latest shared version, so every package that installs it can just use it.
If it's not shared with other products and you're just running into that MSI quirk, then make your own upgrade element and schedule RemoveExistingProducts before CostInitialize, which is what is deciding not to install. That works, but it's before MigrateFeatureStates so you will lose feature migration in your major upgrade.

MSI Installation issue

I am having an MSI file which copies a set of dll from it source to destination folder. While running the MSI the dll are copied to the destination folder also the MSI is installed in the system. I can see that in ADD or REMOVE programs.
Whenever there is a change in dll, i copying the new dll and building the MSI again. When i tries to run MSI in the same system i am getting error "Another version is already. Uninstall that version and proceed" something like that.
What i am doing until now is uninstalling the old one (MSI) and installing the new one.
But i want the MSI to update the older dll with the latest dll from the MSI instead of uninstalling and installing again.
Thanks in advance.
You can't just rerun the MSI to do an update. There's some background here, even if you are not using Visual Studio setups, and it's still relevant after all this time:
https://www.simple-talk.com/dotnet/visual-studio/updates-to-setup-projects/
To replace a single file, build a patch, an msp file. To upgrade the entire setup, including that Dll then use the WiX majorupgrade element, and that may be a lot easier than building a patch, especially if your setup is small and doesn't take long to install. Increase the file version of the Dll to make sure it gets replaced.
welcome to StackOverflow - it seems to be your first post. I would read this thread if I were you to get an overview of how to implement a WIX major upgrade: How to implement WiX installer upgrade?. Here is another thread (didn't read this one through): How to get WiX major upgrade working?
A major upgrade is essentially an automatic uninstall of the existing version and reinstall of a new version. This is the least error prone update mechanism in Windows Installer. It is the recommended approach to try at first - it works well. A minor upgrade - which is upgrading the existing install, is generally more difficult to get right in the beginning. A number of technical restrictions apply. Here is a very good summary of what is required for a minor upgrade to work (as well as other details): http://www.installsite.org/pages/en/msi/updates.htm
Check out this well known wix tutorial for upgrades and patches. And MSDN.