Conditional uninstall of previous version in Wix - wix

I'm doing an installer for an software used for debugging embedded ARM cores (OpenOCD) and this installer automatically removes the previous versions - that's simple. However, sometimes that would be desirable to have more than just one version installed (each version has it's own folder, so there's no conflict here) due to various (in-)compatibility issues etc.
I'm trying to create an installer which would have an option in the Feature tree (or anywhere else) to uninstall (or not) the previous version.
Basically there is this install sequence:
<InstallExecuteSequence>
<Custom Action="NewerVersionDetected" After="FindRelatedProducts">DOWNGRADE</Custom>
<RemoveExistingProducts After="InstallFinalize"/>
</InstallExecuteSequence>
I know that RemoveExistingProducts can be made conditional by putting a condition between RemoveExistingProducts tags but... what should the condition be? There's a lot of info about making features conditional or about conditions like OS version and some registry entries, but I haven't found any useful info about "user-defined conditions"...
Let's say that in the feature tree there is this element:
<Feature Id="UninstallOlderVersionFeature" Title="Uninstal previous versions" Level="1" Description="..."/>
How to make uninstallation previous version conditional on this feature (or any other method the user could select during the installation - a question box or a separate window or whatever it takes)?
Any help appreciated (by me and the users of the installer), as I'm not very good in Wix and XML (I'm an embedded person (; )
If any more details on the whole Wix file are required - tell me and I'll post relevant bits.

Try this:
<RemoveExistingProducts After="InstallFinalize">
<![CDATA[&UninstallOlderVersionFeature=3]]>
</RemoveExistingProducts>
It is the state wether the feature is selected. "3" says that the Feature is selected for installation.

Related

How to restrict user to change feature in case modify and upgrade in installer?

I have a installer which asks the user to select feature. Whatever user selects, it will never be changed in case of modify and upgrade the installation. For example:
There are three features in my installer which are below:
<Feature Id="Standalone" Title="Standalone" Level="2">
</Feature>
<Feature Id="CentralCase" Title="Central case" Level="2" >
</Feature>
<Feature Id="MiddleEF" Title="Middle Ef" Level="2" Display="expand">
<Feature Id="GUI" Title="Client" Level="3"></Feature>
<Feature Id="AppServer" Title="Application Server" Level="3">
</Feature>
</Feature>
Now suppose user starts the installation and select the first feature which is standalone and install it. Now if user wants to modify, he should not allowed to change feature or even if user wants to upgrade, user should also not allowed to change feature. He can only upgrade what he selected at first time. Is there any way to do this?
ARPNOMODIFY: I guess it depends how critical it is that these features never change. You can set the ARPNOMODIFY in the MSI to
1 and there will be no button to invoke Modify from:
<Property Id="ARPNOMODIFY" Value="1" Secure="yes" />
Disclaimer below. Here be dragons.
msiexec.exe: However, you can still invoke modify by launching the MSI file itself (the default dialog sets should correctly disable the modify button though), but worse: you can go via the msiexec.exe command line and change anything you want:
msiexec /i "MySetup.msi" ADDLOCAL=MyFeature
This might be OK since it would appear to be seldomly used. However, you should be aware that remote management systems often rely on the msiexec.exe command line to handle MSI deployment, and as such the deployment system could be used to change feature state easily (via the deployment tool GUI, no command lines to deal with).
Custom Action: I don't know of an auto-magic way to abort setup if the user tries to modify the feature structure invoked via the msiexec.exe command line, but I suppose you can use a custom action maybe right before InstallInitialize in the InstallExecuteSequence to abort the installation if ADDLOCAL, REMOVE or ADVERTISE are set? If you do not condition this custom action properly, it could cause a package that won't uninstall at all or upgrade properly.
Some unverified conditioning suggestions: How to execute conditional custom action on install and modify only?
MigrateFeatureStates: For a major upgrade the GUI will not run as if it is running modify, but a fresh installation (since the product GUID is new). Hence the original installation GUI is shown and not the modify one. Accordingly you might need to disable some GUI controls or hide whole dialogs to prevent feature selection (not sure in WiX default dialogs). Added a link for that below. The standard action MigrateFeatureStates will take care of preserving the feature installation states between versions, provided you haven't done anything drastic to the feature structure. You enable this standard action to run in the Upgrade table. Should be default to run in WiX MSIs I think.
UPDATE:
Preselected Property: There is a special property called Preselected that is to automatically hide feature selection. You can try to set it or check whether it is set automatically by WiX to see if it hides feature selection. I have honestly never tried it.
Some Further Resources:
Hiding whole dialogs: Wix, custom dialog when previous version exists

WiX Custom Action Condition on Property Value

I have a program which is dependent upon postgres. The installer I made will install postgres for the user; however, I would like this to only happen if Postgres is not already installed. I'm attempting to do this through a custom action with conditions, however, I cannot seem to get it to work. Any help would be greatly appreciated. This is what I currently have.
<Property Id="POSTGRESINSTALLED">
<RegistrySearch Id="POSTGRESINSTALLED_SEARCH" Key="SOFTWARE\PostgreSQL\Installations\postgresql-x64-9.5" Root="HKLM" Type="raw" Name="Branding" />
</Property>
<InstallExecuteSequence>
<Custom Action='postgres_install_action' After='vc_redist_install_action'> ( NOT POSTGRESINSTALLED ) OR ( REINSTALL ) </Custom>
</InstallExecuteSequence>
It's not clear which part is not working, the detection or the install.
If you run the install and produce a log (msiexec /I [path to msi] /l*vx [path to text log]) you'll see if POSTGRESINSTALLED_SEARCH is being set. The install doesn't need to complete because the search is early. Assuming you've got the general idea correct, you haven't explicitly said whether to search the 32-bit registry or the 64-bit registry. It may simply be looking in the wrong place.
If the search works, then the install can easily fail. The custom action appears to be immediate (the default) so it will not run elevated and is therefore likely to fail. The same is true of the vc redist install custom action.
The model for installing prerequisites is to use a bundle to install them first. These should help, but that's the way you should be doing this:
http://wixtoolset.org/documentation/manual/v3/bundle/
http://www.c-sharpcorner.com/UploadFile/cb88b2/installing-prerequisites-using-wix-bootstrapper-project-and/
How to include prerequisites with msi/Setup.exe in WIX
WiX - Install Prerequisites and 3rd party applications

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 upgrade scenario

I have an existing WIX installer file that I'm trying to figure out. In this file I see two custom actions defined:
<Custom Action="CreateBackup" Before="InstallInitialize">
<![CDATA[Installed]]>
</Custom>
<Custom Action="RestoreBackup" After= "InstallFinalize">
<![CDATA[NOT Installed]]>
</Custom>
The CreateBackup function copies some files (not directly related to this installer) from a remote location. Restore puts those files back at the same location.
Now in an upgrade scenario I see the following logging order. I have put the apparent value of "Installed" in brackets:
CreateBackup is skipped (Installed == false)
InstallInit
CreateBackup succeeds (Installed == true)
InstallInit
InstallFinalize
RestoreBackup is skipped (Installed == true)
InstallFinalize
RestoreBackup succeeds (Installed == false)
I have a couple of questions about this:
I understand that there is an uninstall and an install going in this script. And based on the value of "Installed" I conclude that the install is done first. Is this correct?
I see that the InstallInit is called twice before the first InstallFinalize. What does this mean? Is the installation still busy when uninstall begins?
The first value of Installed is false, so I guess it is relative to the new version? But how does it become false again after the deinstallation finishes? Is it relative to the old version then?
I am using an MajorUpgrade element.
Hope someone can clear this up.
I'm assuming that you are using a WiX MajorUpgrade element to do your upgrade, so the conditions you need should be something like this:
When you are doing an upgrade the WIX_UPGRADE_DETECTED property is set when an upgrade is being done:
http://wixtoolset.org/documentation/manual/v3/xsd/wix/majorupgrade.html
So use that condition when you want to create the backup, assuming that you want to do the backup of existing files (from the older product) when doing the upgrade.
It's not clear from your post exactly when you want to do the restore but if it's after the upgrade then use the same WIX_UPGRADE_DETECTED property.
Those conditions based on the Installed property don't seem to make a lot of sense because the property is set if the current MSI's ProductCode is installed. In a upgrade at that stage it will always be unset.
This post has more info about properties and install actions:
How to add a WiX custom action that happens only on uninstall (via MSI)?

Wix: moving <Upgrade> into <Fragment>

I'm working on a suite of related applications that all have their own WiX installer. As you can imagine, there's a lot of duplication between these installers, and I'm trying to do something about that. To this end, I've created a WiX Library Project with some .wxs files, and I've been trying to shove as much code as possible into these .wxs files. Which has worked mostly fine, so far. However, there's one thing that I can't seem to move to a .wxs: the Upgrade element.
This is my upgrade code:
<Upgrade Id="MY-UPGRADE-CODE-GUID">
<!-- If an older version is found replace it -->
<UpgradeVersion OnlyDetect="no" Property="OLDERFOUND"
Maximum="$(var.ProductVersion)" IncludeMaximum="no"/>
<!-- If the same version is found do nothing (OnlyDetect) -->
<UpgradeVersion OnlyDetect="yes" Property="SELFFOUND"
Minimum="$(var.ProductVersion)" IncludeMinimum="yes"
Maximum="$(var.ProductVersion)" IncludeMaximum="yes"/>
<!-- If a newer version is found do nothing (OnlyDetect) -->
<UpgradeVersion OnlyDetect="yes" Property="NEWERFOUND"
Minimum="$(var.ProductVersion)" IncludeMinimum="no"/>
</Upgrade>
<CustomAction Id="AlreadyUpdated" Error="This version of !(wix.Param.Upgrade.ProductName) is already installed. You will have to uninstall it manually before this installer can continue."/>
<CustomAction Id="NoDowngrade" Error="A newer version of !(wix.Param.Upgrade.ProductName) is already installed. You will have to uninstall that version manually before this installer can continue."/>
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallInitialize"/>
<!-- If the same version is found execute AlreadyUpdated -->
<Custom Action="AlreadyUpdated" After="FindRelatedProducts">SELFFOUND</Custom>
<!-- If a newer version is found execute NoDowngrade -->
<Custom Action="NoDowngrade" After="FindRelatedProducts">NEWERFOUND</Custom>
</InstallExecuteSequence>
For Upgrade/#Id, I use the same value as for Product/#UpgradeCode. (I'm not sure whether that's necessary, or even good practice.) I'd like to put that in a WixVariable at some point but for now I'm trying to make it work with literal GUIDs.
I have already moved the CustomActions and the InstallExecuteSequence to a Fragment in my .wxs file. I have defined a WixVariable to contain the ProductName, so I can have each app's installer display its own name. This works exactly the way I want and expect.
However, when I move the Upgrade element into a Fragment (it doesn't matter if it's in the app's Product.wxs file or in one of the library .wxs files, and it also doesn't matter if the Fragment is the same one that contains the InstallExecuteSequence), the following problems occur:
If I first run the old version of the installer, and then the new one with Upgrade moved into a separate Fragment, but with identical Product/#Version, Product#/UpgradeCode, and Upgrade/#Id, I end up with 2 installs of the same product, which is obviously not what I want.
If I install the new installer twice, it won't install the second time (which is what I want), but it won't show my error message either.
I would replace the whole thing with a MajorUpgrade, were it not for the facts that 1) it doesn't seem to want to give an error message when the same version is installed again, and, more importantly, 2) it's not allowed inside a Fragment for some reason, so I'm still stuck with duplication (albeit a lot less than previously).
So ... how can I move my Upgrade element into a .wxs library without losing the current functionality? What am I doing wrong?
As described in the wix Linker documentation, the linker will follow references between fragments while building a windows installer database. But this means that it will ignore any fragments that aren't referenced (directly or indirectly via other fragments) by the product wxs.
This behavior can be quite useful in your scenario, because it allows you to model the dependencies between all your reusable components, and the linker will make sure to include only the ones that are needed for a specific product.
To make sure that your upgrade logic is included, you have two alternatives:
Add a dummy Property to your fragment, and reference it with a PropertyRef in your product wxs.
Or put your upgrade logic in an Include element, and then instruct the wix preprocessor to include it in your product wxs. See the Wix Preprocessor documentation for an example.