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.
Related
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?
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?
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.
As part of an WiX-based installer I have the following component with a User element (besides other components with File, ServiceInstall and ServiceControl elements):
<Component Id="cmpServiceUser" Directory="TARGETDIR" Guid="PUT-GUID-HERE">
<util:User
Id="ServiceUser"
Name="[ProductName]"
Password="[ServiceUserPassword]"
PasswordNeverExpires="yes"
LogonAsService="yes"
CreateUser="yes"
UpdateIfExists="yes"
RemoveOnUninstall="yes"
/>
</Component>
I have decided to go the only-major-upgrades way and placed the following element inside the Product:
<MajorUpgrade
Schedule="afterInstallInitialize"
DowngradeErrorMessage="A newer version of [ProductName] is already installed."
/>
The first installation of the product works as expected. During a major upgrade, I would have expected the user to be first removed (during RemoveExistingProducts) and later being created again. But it behaves different: The user is really removed nearly at the end of the installation - after the service for example has been restarted again (Process Explorer can still tell me the service's user name but not the SID any more - as the service's user was removed after the service was started).
After many hours of dissecting various installation logs I have turned to peek into the sources of the UtilExtension and probably found an explanation in scauser.cpp (slightly reformatted for better SO-readability):
// Schedule the removal because the user exists and we don't have any flags set
// that say, don't remove the user on uninstall.
//
// Note: We can't rollback the removal of a user which is why RemoveUser is a
// commit CustomAction.
So I guess this late removal of the user really belongs to the RemoveExistingProducts action in this case.
Thanks for reading this far - now the question: Is it possible to use util:User#RemoveOnUninstall in collaboration with a major upgrade strategy?
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.