Issue with wix installer upgrade from net472 to net6.0-windows app - wix

We have migrated our app from net472 to net6.0-windows. There is a wix installer for the app.
Currently the installer's upgrade sequence from net472 to net6.0-windows doesn't work as expected.
This is because there is a bunch of third party libs which have have the same file version for different platforms in net472 and net6.0-windows. e.g. NLog.dll would be 4.7.7.12472 in both platforms.
AFAIU, the file replacement rules work so that when source file(assembly in installer) and target file(installed assembly) version are equal, the installer will not replace this file.
So now after the upgrade, the bin folder is left with a mix of old(net472 from 3rd parties) and new(mostly our 'file version incremented' net6.0-windows) assemblies.
Any recommendations on how to solve this upgrade problem?

This is an example of broken component rules (by third parties). The windows installer thinks it's good because the versions are the same but the files are not forward or backwards compatible.
Unless you want to set the REINSTALLMODE property to amus (forced overwrite of everything ) or do file by file version lying (file version attribe 65535.65535.65535.65535 to force that file to always overwrite ) you will need to do a MajorUpgrde scheduled afterInstallValidate.
Other possibilities is to treat this as a new product and install to a new directory or consider alternate .NET compile/publish options so that the app self contains those files without the installer ever seeing them.

Related

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

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.

WiX - VC++ Runtime - vcredist vs merge module

I am building a MSI installer using WiX. As the product that is supposed to be installed requires a VC++ runtime I found the two option of doing that which are (a) using the corresponding merge module and (b) using burn to run vcredist prior to the actual product's MSI. To differ between those two options I have some questions I didn't find the information on.
1) If using the merge module the runtime is not installed separately (no entry in add and remove programs) but as far as I understand just copies the required files to their corresponding locations.
1.1) If there already is an installation of vcredist present on the system I suppose the merge module does nothing during the product's installation?
1.2) If there was a vcredist installation present as mentioned in 1.1) what happens if the user just uninstalls that runtime via add and remove programs? I suppose the product does not work anymore (at least until a repair installation is performed which causes the merge module stuff in the MSI to re-add the necessary files?)
1.3) If the vcredist was not yet installed and the MSI installer copies the files to the system and after that another installer or the user is calling the vcredist.exe what is happening then? Does vcredist reports the runtime as already being installed?
1.4) If uninstalling the product are the files copied to the system via the merge module being removed?
1.5) If the answer to 1.4) yes (VC++ runtime files are removed): If there are two different products are installed on the system both using the same merge module internally and one of the two products is uninstalled what happens to the installed runtime files? Are they removed and the other product is not working anymore due to the missing runtime files?
Thanks in advance for your help and information.
Regards,
Gunnar
A merge module is just a consistent way to install (usually shared) files properly by including them in a build. As an analogy, the C++ compilers build .obj files that are combined into an exe, but you can no longer talk about an obj having a separate existence after the exe is built and running. It's the same with merge modules. Once they are in the MSI they are just files to install, and they follow the standard rules. If a file is already installed (in the shared location) it will be overwritten if the incoming version is higher than the already installed version. Its containing component will be ref counted up by one, and uninstalling other products (including the VC redist) using the component will ref count down so that any products using the file don't break - there is still a version for them to use.This is just the standard sharing method for files shared amongst products, and it makes no difference that one product is a VC redist and the other is your MSI. It could just as easily be several of your products that each install the files using merge modules and each can be uninstalled without breaking the others because of the ref counting and that fact that the shared location and common component ID makes the sharing work as intended. So forget that one product is the VC redist and the other is yours (or another that uses the same files) - it's all just the Windows Installer's shared file/ref counting mechanism.
This is for the recent merge modules that install the files to the system folder. There have been other schemes using WinSxS and policy redirects that don't seem to be used now (VS 2015).

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.

How to include files that changed but have the same version number into MSP using WiX Pyro?

I created an MSP patch file using WiX Pyro(using purely WiX toolset). But it seems that created MSP file doesn't include files changed in content but not changed in version number. As I think, most of those files are not actually changed in main content but just rebuilt by Visual Studio. So, it may be desirable behavior that Pyro does.
But my customer asked me why files installed by the latest MSI and files updated by MSP patch are different. So, I want to know there is any way to include those files into MSP patch, or can I tell my customer that those two file sets(the latest MSI, old MSI + MSP) are the same so you can use the product safely? (Of course, I think my file version management is pretty poor and not systematic, but MSI's version rules feel too strict and somewhat inflexible to me.)
The Windows Installer file update rules are what they are, and I don't think you're going to get them changed. The standard rule is that you tell Windows the file is updated by incrementing the version, and then at install time a higher version will replace a lower version. That's the reason tools that generate patches also use the version rule - why should they include files that haven't got a higher version? Service packs, patches, hotfixes etc all work based on file versions.
If you want to use whatever file versions you like, then stop using patches and do a major upgrade that does a RemoveExistingProducts early so you get the equivalent of an uninstall of all the old files followed by an install of all the new ones.
By the way, if these are managed code files and you believe that incremnting the file version means incrementing the assemblyversion, then use AssemblyFileVersion to increment the file version and leave assembly version alone.