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

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?

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 that runs when install fails

I have a simple Wix project and I want to add the custom action that will run no matter what the result of the install is - success, error, cancel etc.
I've tried CustomAction with Execute="rollback" - it doesn't seem working.
In order to check if my custom actions are called on error I've added this custom action to generate an error (it returns ERROR_INSTALL_FAILURE from the DLL):
<CustomAction Id="TestBad" BinaryKey="testDll" DllEntry="TestBad" HideTarget="yes" Impersonate="no" Execute="immediate" Return="check"/>
and didn't find even one way to call the custom action on error.
Is there any way to do this?
EDIT
I've forgot to mention that I need it to run at the end of install process - I want to know the result of the installation process.

Understanding MSI uninstall - EXECUTEACTION = INSTALL

I'm having issues with with a custom action I want to run during uninstall.
The custom action is responsible for unregistering a system level windows service.
Custom action:
<CustomAction Id='UnregisterService'
Directory='INSTALLDIR'
ExeCommand='[INSTALLUTIL] /u "/config=[INSTALLDIR]\configInstall.xml" " ...
Return='ignore'
Execute='deferred' />
This action scheduled in the InstallExecuteSequence and should be invoked on uninstall and upgrade:
<InstallExecuteSequence>
<Custom Action="PreventDowngrading" After="FindRelatedProducts">NEWPRODUCTFOUND</Custom>
<RemoveExistingProducts After="InstallValidate" />
<Custom Action='UnregisterService' After='InstallFiles'>UPGRADEFOUND OR (Installed AND NOT UPGRADINGPRODUCTCODE)</Custom>
<Custom Action='RemAgApps' After='UnregisterService'>UPGRADEFOUND OR (Installed AND NOT UPGRADINGPRODUCTCODE)</Custom>
...
</InstallExecuteSequence>
The problem is that when I run uninstall from the ARP, uregister action does not invoked (as written in msi logs):
Skipping action: UnregisterService (condition is false)
When I dig deeper into the log (which is terrible...) I see that there is no 'uninstall' action, only install:
Property(C): EXECUTEACTION = INSTALL
Property(C): ACTION = INSTALL
Product: BLABLA -- Installation completed successfully.MSI (c) (BC:50) [14:35:37:067]: Windows Installer installed the product. Product Name: BLABLA . Product Version: 5.1.0. Product Language: 1033. Manufacturer: BLABLA Team. Installation success or error status: 0.
My questions are :
What is the flow of msi uninstall? (No couldn't find it in the web...)
Why I get the 'Install' action during uninstall?
How can I force uninstall to run my 'unregisterService' custom action?
Thanks!
Custom actions should be avoided until you fully understand Windows Installer to avoid reinventing the wheel with a more fragile solution. To remove a service during uninstall you merely need:
<ServiceControl Id="myService" Name="MyService" Stop="both" Remove="uninstall"/>
Read this good article: InstallSite: Installation Phases and In-Script Execution
Windows installer installation transactions can take many forms. Initial install, repair, maintenance install, uninstall, upgrade, patch and so on. The installer tracks the state change of features, components and products at different levels so the concept of install/uninstall is more detailed.

Wix installer major upgrade custom action before removing old version

I'm using wix installer (version 3), I have an msi of version 1.99 and another msi of version 2.00. My app has the ability to do import and export of the DB by calling it with some arguments. I'm trying to perform a major upgrade, and trying to call custom actions before and after the upgrade.
Now, the custom action code works just fine. The problem is, that the code that should run BEFORE the old version is removed, is running AFTER it is removed, and thus cannot activate the app and produce the backup files.
In short: How do I time the custom actions to do their work before the removal of the old version?
This is how I call them:
<CustomAction Id="doExport"
Return="check"
Execute="immediate"
BinaryKey="ImportExportBinary"
DllEntry="BeforeInstall" />
<CustomAction Id="doImport"
Return="check"
Execute="immediate"
BinaryKey="ImportExportBinary"
DllEntry="AfterInstall" />
<InstallExecuteSequence>
<Custom Action="doExport" Before="InstallInitialize"> NOT Installed</Custom>
<Custom Action="doImport" After="InstallFinalize"> NOT Installed</Custom>
</InstallExecuteSequence>
EDIT:
Here is the major upgrade code:
<MajorUpgrade AllowDowngrades="no"
Schedule="afterInstallFinalize"
DowngradeErrorMessage='Cannot downgrade!'
AllowSameVersionUpgrades='yes' ></MajorUpgrade>
I've tried playing a bit with "Execute" attribute of the CustomAction element, without any results.
First, do your upgrade creating a verbose log to make sure your custom actions are working and being called. You've marked them immediate, so they run before anything changes on the system and so will be called before the old product is removed. When you say "the code works just fine" you probably mean when you run it from your interactive account. But that's not happening. Your code is running out of an msiexec.exe process, the working directory is not what you expect, your code may not be looking in the right place for the files, it will not be elevated and so may not be able to do what it thinks it can. There are many opportunities for your code to not work as expected.
If you ever marked them as deferred I can see why doExport might not work. Without seeing your MajorUpgrade element I can't be sure, but the default scheduling for RemoveExistingProducts is afterInstallValidate. Your custom action is before InstallInitialize, so the actual sequence in the MSI file could easily be InstallValidate, RemoveExistingProducts, doExport, InstallInitialize.
and RemoveExistingProducts that does the uninstall of the old version is before your custom actions.
So if you want to use execute deferred, try Before="RemoveExistingProducts" on your doExport, or use Schedule in your MajorUpgrade to afterInstallInitialize and keep your doExport before InstallInitialize.
I tryied the same thing, and it took me long to find a way for it to work.
You can use
<Custom Action="doExport" Before="RemoveExistingProducts"></Custom>
The option RemoveExistingProducts refers to the action of uninstalling the existing version of your product, so you have to execute the action before the existing version is removed.
I did not find this option in this list: https://learn.microsoft.com/en-us/windows/win32/msi/suggested-installexecutesequence
But it seems to work, and executes before the list of options described in the link.
About the options which should go inside the <Custom></Custom> tag, I am not sure, but I hope it helps to start the code at the right moment.

How can I perform a custom action in WiX that only executes on install or uninstall?

I have two custom actions, one of which I'd like to execute when my product is installed and the other when it is uninstalled.
My custom action is currently called from a merge module after the "InstallFinalize", but this happens for both install and uninstall. I've tried adding the "NOT INSTALLED AND NOT UPGRADINGPRODUCTCODE" condition, but that gave me an error:
Error 2762. Cannot write script record. Transaction not started.
I've also tried attaching to other actions (for example, UnpublishComponents), but I can't find any that are unique to install or uninstall.
How can I fix this problem?
Try next
1. Only for Installation:
<InstallExecuteSequence>
<Custom Action="SomeAction" After="InstallFinalize">NOT Installed AND NOT REMOVE</Custom>
</InstallExecuteSequence>
2. For Uninstall try to use: Rob's answer
UPGRADINGPRODUCTCODE property is set only during RemoveExistingProducts CA.
The variable “INSTALLED” should be using is “Installed”. Find more information regarding the Install and uninstall conditions in the Stack Overflow answer How to add a WiX custom action that happens only on uninstall (via MSI)?.