MSI / Wix installer, install or upgrade - wix

I managed to create a MSI who can install or upgrade my product. But I want to go further and display messages like Your version is already up to date / An upper version is already installed.
In order to do this, I put this in my code :
<Upgrade Id="{{MYGUID}}">
<UpgradeVersion Minimum = "0.0.0"
Maximum = "{{MY_CURRENT_VERSION}}
IncludeMinimum="Yes"
IncludeMaximum="No"
Property="UPGRADEOLDERVERSION"/>
<UpgradeVersion Minimum = "{{MY_CURRENT_VERSION}}
OnlyDetect = "Yes"
IncludeMinimum = "Yes"
Property="UPTODATE"/>
<UpgradeVersion Minimum = "{{MY_CURRENT_VERSION}}"
Maximum = "99.99.99"
IncludeMinimum="No"
IncludeMaximum="No"
Property="UPPERVERSIONINSTALLED"/>
</Upgrade>
<Condition Message = "Already up to date">UPTODATE</Condition>
<Condition Message = "Upper version already installed">UPPERVERSIONINSTALLED</Condition>
And it works well. But the thing is that I want the soft to be installed if it's not the case, and by adding theses Condition, whenever I try to install the software, the messages pop up and don't allow me to install the soft, unless I remove theses ones.
The idea is to do something like
if(not installed)
# Apply the update rules
else
install soft
How can I do this ? Thanks !

Keep in mind that you can't install the same MSI twice, just in case that's what your "already up to date" message is about. Anyway, you can't use properties derived from an upgrade as launch conditions because the fact of an upgrade (or not) does not get resolved until the FindRelatedProducts action has run, which is after launch conditions. So your properties have no value (=false) and prevent the install.
The short answer is that I think that if you use the WiX MajorUpgrade element it will do what you want, allowing upgrades but not downgrades. If you don't want to do that, then display your messages (or add them to a dialog) somewhere after FindRelatedProducts has run in the UI sequence.

Related

Windows Installer does not install feature and does not report an error. (Request: Null)

A simple msi created using WiX. Trying to install a product. On some computers it will fail, without an error, and the installer log will contain these rows:
PROPERTY CHANGE: Adding WIX_UPGRADE_DETECTED property. Its value is '{7C9...0A0}'
PROPERTY CHANGE: Adding MIGRATE property. Its value is '{7C9...0A0}'
Product Code from property table after transforms: '{68F...FAE}'
Product not registered: beginning first-time install
Doing action: INSTALL
Doing action: InstallValidate
Feature: ProductFeature; Installed: Absent; Request: Null; Action: Null
MIGRATE = {7C9...0A0};{A9B...BCE}
Installation completed successfully.
Somehow, Windows Installer decides that there is nothing to do (Request: Null), even though it acknowledges that this is the first time install. That computer had previous version of the product installed. How do I force the installer to install?
Here is the relevant WiX code:
<Product Id="*"
Name="..."
Language="1033"
Version="..."
Manufacturer="..."
UpgradeCode="8D8...196">
<Package Id="*"
InstallerVersion="200"
Compressed="yes"
InstallScope="perMachine"
InstallPrivileges="elevated"
Platform="$(var.Platform)" />
<Property Id="REINSTALLMODE" Value="amus" />
<MajorUpgrade AllowDowngrades="yes" IgnoreRemoveFailure="yes" AllowSameVersionUpgrades="no"/>
<Feature Id="ProductFeature" Title="..." Level="1" Absent="disallow" AllowAdvertise="no" InstallDefault="local" TypicalDefault="install">
<Condition Level="1">True</Condition>
Full installer log: https://www.dropbox.com/s/abnryavh203a82w/acme_installer_log.txt?dl=0
You did not set MigrateFeatures in your MajorUpgrade element. The documentation says the default value is Yes.
http://wixtoolset.org/documentation/manual/v3/xsd/wix/majorupgrade.html
The log shows you are doing an upgrade, therefore the only features that are going to be installed is the same set that are already installed.
It's not clear how many features there are in your product, but if you always want all features to be installed during an upgrade set MigrateFeatures to No.
Either way, it would be better to supply the entire log to see what's going on and if there is another explanation.
This got too long for a comment, adding as an answer. Just a couple of things:
You definitely should not set REINSTALLMODE="amus" for any MSI package.
If you include merge modules in this setup you could downgrade shared files - even if you otherwise don't write to shared locations yourself.
The best would be the default "omus", but I guess "emus" is also ok (Reinstall if the file is missing, or is an equal or older version).
It certainly looks like the MigrateFeatureStates action is at least related to the problem seen:
MigrateFeatureStates: based on existing product, setting feature 'ProductFeature' to 'Absent' state.
Your INSTALLLEVEL property looks fine.
You might want to open the compiled MSI and verify that there are no entries in the Condition table (which can change feature selection states). There should be nothing there, but perhaps just verify.
I suppose you could try to set the Absent attribute for the WiX Feature element to "disallow", recompile and see if this changes things. If this is your only feature I would make this change in addition to using Phils suggestion to set MigrateFeatureStates to "No". Why allow your only feature to be removed?
It is a little odd how this problem has occurred. Perhaps you ran modify on your MSI and simply selected the feature to be uninstalled in the old installation on the machine? Sounds unlikely - especially if it is your only feature.
Did you install several earlier versions of your product on the machine in question, or was there only one prior version?
Again, are there entries in the "Condition table" in the compiled MSI? (Check using Orca).
Did you change the feature name in your newest package?
In any event you should be able to "fix" the broken install by 1) simply running your existing setup and adding ADDLOCAL=ALL to your msiexec.exe install command - I think that will override the MigrateFeatureStates action (not a real fix, just a workaround). Or 2) you can disable MigrateFeatureStates and recompile the setup - that should fix things permanently, and 3) if I were you I would set the Absent attribute for the WiX Feature element to "disallow" so that your (only?) feature can't be removed.
Phil, if you see this, do you know what the MIGRATE property seen in the log is about? It looks like some WiX specific stuff - unless it is something from this MSI's upgrade table:
MSI (c) (04:24) [13:01:38:751]: Doing action: FindRelatedProducts
MSI (c) (04:24) [13:01:38:751]: Note: 1: 2205 2: 3: ActionText
Action 13:01:38: FindRelatedProducts. Searching for related applications Found application: [1]
Action start 13:01:38: FindRelatedProducts.
FindRelatedProducts:
MSI (c) (04:24) [13:01:38:751]: PROPERTY CHANGE: Adding WIX_UPGRADE_DETECTED property. Its value is '{7C945F97-A2CC-4EEB-87AC-3D23D6C490A0}'.
MSI (c) (04:24) [13:01:38:751]: PROPERTY CHANGE: Adding MIGRATE property. Its value is '{7C945F97-A2CC-4EEB-87AC-3D23D6C490A0}'.
FindRelatedProducts:
MSI (c) (04:24) [13:01:38:752]: PROPERTY CHANGE: Modifying WIX_UPGRADE_DETECTED property. Its current value is '{7C945F97-A2CC-4EEB-87AC-3D23D6C490A0}'. Its new value: '{7C945F97-A2CC-4EEB-87AC-3D23D6C490A0};{A9B1787A-32E4-4D51-819D-B3F879574BCE}'.
MSI (c) (04:24) [13:01:38:752]: PROPERTY CHANGE: Modifying MIGRATE property. Its current value is '{7C945F97-A2CC-4EEB-87AC-3D23D6C490A0}'. Its new value: '{7C945F97-A2CC-4EEB-87AC-3D23D6C490A0};{A9B1787A-32E4-4D51-819D-B3F879574BCE}'.
Action ended 13:01:38: FindRelatedProducts. Return value 1.
tandrasz: as far as I can see the two GUIDs that are found by FindRelatedProducts ({7C945F97-A2CC-4EEB-87AC-3D23D6C490A0};{A9B1787A-32E4-4D51-819D-B3F879574BCE}), are different from your new setup's product code ({68F5E655-2E2D-492E-B445-BE1650AF5FAE}). This makes it look like there are two "related products" found by FindRelatedProducts on the machine in question. Are two older versions of the same MSI installed, or do you have a "suite" of MSI files that all share the same upgrade code?. If you have two "unrelated" packages installed, perhaps MigrateFeatureStates finds no way to migrate the feature states between those two and the new package you are installing?

Wix CustomAction with After='InstallFinalize' executed in upgrade to next version?

I am using the following code to start a Setup program at the end of my Installation (also with elevated permissions):
<Product ... >
...
<InstallExecuteSequence>
<Custom Action='LaunchSetupAction' After='InstallFinalize'/>
</InstallExecuteSequence>
<!-- Workaround required for Windows7, Setup.exe can not be started directly because of UAC
http://stackoverflow.com/questions/2325459/executing-a-custom-action-that-requires-elevation-after-install -->
<Property Id="WixShellExecTarget" Value='[#SetupEXE]'/>
<CustomAction Id="LaunchSetupAction" BinaryKey="WixCA" DllEntry="WixShellExec" Execute="immediate" Return="check" Impersonate="yes"/>
...
<MajorUpgrade DowngradeErrorMessage="A later version of [ProductName] is already installed. Setup will now exit." />
</Product>
This works well when then program is installed freshly, but when it is upgraded from a previous version with the same code, the installation reports a fatal error and fails.
In the Windows installer log I have found the following:
Action start 23:42:22: LaunchSetupAction.
...
MSI (s) (38:88) [23:42:22:342]: Invoking remote custom action. DLL: C:\WINDOWS\Installer\MSI679.tmp, Entrypoint: WixShellExec
...
WixShellExec: WixShellExecTarget is C:\Program Files\Me\MySetup.exe
...
WixShellExec: Error 0x80070002: ShellExec failed with return code 2
...
WixShellExec: Error 0x80070002: failed to launch target
...
Action ended 23:42:22: LaunchSetupAction. Return value 3.
Action ended 23:42:22: INSTALL. Return value 3.
Interesting enough these error messages (file not found?) is also there when I comment out the CustomAction in the new version of the installer.
Why is this CustomAcount executed from the previous version to be uninstalled?
Is there any way I can stop it from being run? After a certain point of the upgrade, the MySetup.exe is just not there anymore and will always cause a fatal error ...
That custom action has no condition on it, so it will be called on all types of installer operations, such as uninstall, add/remove features etc. Your upgrade is an install of the new product and an uninstall of the old product.
You need this kind of thing on your custom action:
Wix: Run custom action based on condition
so a condition of 'Not Installed' causes the CA to run only if the product is not already installed.
You can't really fix this because the uninstall for that product is embedded in the system. You'd need to fix that older version of the product by rebuilding the exact same product with the custom action condition fixed and then fix it with:
msiexec /i [path to new MSI] REINSTALL=ALL REINSTALLMODE=vomus
If you're very familiar with MSI and Orca, you may be able to locate the cached MSI file in C:\windows\installer, open it with Orca, go the InstallExecuteSequence and give that custom action a condition of 0, then save it. That basically corrects the uninstall sequence so that your upgrade will work. Caveat Emptor.
It also appears that a plain uninstall would give you same error - did you every try to do an uninstall test?

Is there an updated truth table for WiX custom action conditions?

I am trying to get some custom actions working properly on my install.
This is the table I am using for my logic in Wix (provided by ahmd0)
The problem is that it just doesn't seem to be working. Here are my conditions currently:
<Custom Action='RemoveService' After='InstallInitialize'>Installed</Custom>
<Custom Action='WaitForFileLocks' After='InstallInitialize'>Installed</Custom>
<Custom Action='InstallService' Before='InstallFinalize'>NOT REMOVE ~= "ALL" OR UPGRADINGPRODUCTCODE</Custom>
<Custom Action='MergeConfigFiles' Before='InstallFinalize'>NOT REMOVE ~= "ALL" OR UPGRADINGPRODUCTCODE</Custom>
The behavior I expect from this:
RemoveService and WaitForFileLocks should be run on uninstalls and upgrades.
InstallService and MergeConfigFiles should be run on installs and upgrades.
The behavior I am seeing:
RemoveService and WaitForFileLocks are running on uninstall, but NOT upgrades.
InstallService and MergeConfigFiles is being run on install, but NOT upgrades.
Is the table I am using wrong? Is my logic correct? Could my issue be Windows' recognition of what an "upgrade" is? I checked in Add/Remove programs after I increased all 3 version numbers for my install and the new version is properly updating there.

How to check an installed version of a product from MSI

After review a lot of posts in this site finally I decide to put mine.
I am preparing an MSI file with Wix.
I need to check if a particular version of an enterprise product is installed, before to install my system. I have the GUID of that product (which is the same for all versions), but I need to check if 1.10.0 version is installed.
Any idea, please.
Thanks in advance.
PD: I am newbie in Wix, so at this moment I am just using the wxs file created by default with the Setup Project.
Clarifying: I don't want to upgrade the software that I am installing, I need to check another program and version which my installer depends.
To do it properly, you probably want to use a custom action. Inside the custom action, use the MsiGetProductInfo function.
A way of doing it in pure-WiX would be to modify the example found here: How do I compare registry versions in WiX?
First create a RegistrySearch element:
<Property Id="PRODUCTVERSION">
<RegistrySearch Id="ProductVersionSearch" Root="HKLM" Key="software\Microsoft\Windows\Current Version\Uninstall\[PRODUCTCODE]" Name="DisplayVersion" Type="raw" />
</Property>
Then use a Condition element:
<Condition Message="Product version 1.10.0 must be installed">
<![CDATA[PRODUCTVERSION AND PRODUCTVERSION = "1.10.0"]]>
</Condition>
This would search for exactly version 1.10.0, so may not be what you want if you're looking for something like "v1.10.0 or newer"... But should get you started.
Perhaps try the proposed solution in this post: WiX Installer: getting version of the product being upgraded
It involves using the Upgrade table to identify the installed product, and a custom action using VBScript to determine the version.
If you want to do something like create an error message or fail the install if that version is present you can have multiple upgrade entries. Have one that has something like this, bad syntax...
<Property Id="VERSION110INSTALLED" Secure="yes" />
<Upgrade Id="YOUR_GUID">
<UpgradeVersion
Minimum="1.10.0" Maximum="1.10.0"
Property="VERSION110SINSTALLED"
IncludeMinimum="yes" IncludeMaximum="yes" OnlyDetect="yes" />
</Upgrade>
Then you have that property set if version 1.10.0 is present, and if you want to produce an error message condition it on VERSION110SINSTALLED, and sequence it after FindRelatedProducts.

WIX MajorUpgrade and return code

I am writing an installer that also contains MSI package built using WIX. I check the return code after running msiexec and skip some error codes. For example user should't receive error when return code is 1638 (product has already installed)
But when the older version of product was installed the return code is 1603 (unknown error). I also want to skip this situation (user already get newer version)
Wix code is
<Upgrade Id="<GUID>">
<UpgradeVersion Minimum="$(var.product_version)" OnlyDetect="yes" Property="NEWERFOUND" />
<UpgradeVersion Minimum="0.1.0" IncludeMinimum="yes" Maximum="$(var.product_version)" IncludeMaximum="no" Property="SELFFOUND" />
</Upgrade>
<MajorUpgrade DowngradeErrorMessage=You have installed newer version $(var.product_name)." />
The question is how I can return another code than 1603 when SELFFOUND or NEWERFOUND properties are activated
Update: I didn't want to influence the return code, I want to get appropriate code instead the general fatal error
You can't control the return code of msiexec.exe. Its return codes are documented and the list is comprehensive. The service determines the exit code based on the status of installation.
Update: The only thing I can suggest is to use database functions directly:
MsiOpenPackage, and
MsiDoAction("FindRelatedProducts") to run FindRelatedProducts action which will evaluate SELFFOUND and NEWERFOUND properties by processing Upgrade table.
Then analyze the properties with MsiGetProperty.
Close the package with MsiCloseHandle.
Using the obtained values of SELFFOUND and NEWERFOUND, you can decide whether you need to install the product or not. If yes, you can run msiexec.exe or use MsiInstallProduct.
Disclamer: I have never tried to do anything like this, and I am not sure it will work. It should, yet it does not look easy.