I have a requirement wherein I need to execute a custom action if the installer fails (either automatically or manually failed by returning ActionResult.Failure from another custom action).
I tried
<Custom Action="CallMe" After="InstallFinalize"></Custom>
but the CA is not called.
Any help is appreciated.
Edit: Found out from logs that it is "FatalError" custom action. But then doing this <Custom Action="CallMe" Before="FatalError"></Custom> throws
Error 8 Unresolved reference to symbol 'WixAction:InstallExecuteSequence/FatalError' in section 'Product:*'
You can use
<Custom Action="CallMeCancel" OnExit="cancel" />
<Custom Action="CallMeError" OnExit="error" />
The values for OnExit are success, cancel, error, suspend
Related
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.
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.
I've got a custom action I've defined for my installer. The installer doesn't seem to be running.
Here's the lines in the WXS file that define the custom action:
<CustomAction Id="GetConfigProperties" BinaryKey="GetPropertiesDLL" DllEntry="GetPropertiesFromConfigFile" />
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallInitialize" />
<Custom Action="NewerVersionDetected" After="FindRelatedProducts">NEWERVERSIONDETECTED</Custom>
<Custom Action="GetConfigProperties" After="FindRelatedProducts"></Custom>
. . .
</InstallExecuteSequence>
<Binary Id="GetPropertiesDLL" SourceFile="$(var.LPRCore Installer CBP Helper.TargetDir)\LPRCore Installer CBP Helper.CA.dll" />
I've checked the MSI with Orca and the appropriate entries are in the MSI's tables.
Here's an excerpt of the code in the CustomActions.cs file:
[CustomAction]
public static ActionResult GetPropertiesFromConfigFile(Session session) {
// Output a start message to the install log
session.Log( "Begin GetPropertiesFromConfigFile" );
. . .
return ActionResult.Success;
}
There are a few other session.Log statements in the code at places where I wanted to see what was going on.
Now, I have logging enabled. When I look at the log file in Notepad, I see no messages from my calls to session.Log. I see no references to GetConfigProperties either. It appears that the custom action is not executing at all. What have I done wrong?
It turns out that the custom action wasn't running because:
It was scheduled to run in the wrong place. My fault, I needed to put it in the InstallUISequence section, not the InstallSequence section.
I was aborting the install before the action could run.
When I put the custom action into the InstallUISequence section and in the right place, everything ran fine.
Thanks for trying.
Tony
In case you don't see any entries of GetConfigProperties custom action in your log file, most likely the reason is that InstallExecutesequence element resides in a separate Fragment, which is not included into the package. To include the contents of the Fragment into the package, you should reference any element in it from inside the Product element.
For instance, you can add the following line to the Product element:
<CustomActionRef Id="GetConfigProperties" />
I think you are missing condition on which custom action should run. Either give some condition <Custom Action="GetConfigProperties" After="FindRelatedProducts">NOT INSTALLED AND NOT REMOVE</Custom> or if you want to make it default then put 1 as condition
<Custom Action="GetConfigProperties" After="FindRelatedProducts">1</Custom>
We have a custom action that we want to run ONLY on a major upgrade. Trouble is, the CA is deferred because it needs access to system files (it removes the old version of the program from an INI file). Because the CA is deferred, it only has access to the CustomActionData property.
So my first attempt was to set CustomActionData if UPGRADINGPRODUCTCODE was true, and then condition the CA that actually did the upgrade on CustomActionData. That failed--CustomActionData was not set, I assume because it's only visible within the custom action and not usable as a condition for the custom action.
Here are the CA's I defined:
<CustomAction Id="MyCA.SetProperty" Return="check" Property="MyCA"
Value="[UPGRADINGPRODUCTCODE]" />
<CustomAction
Id="MyCA"
BinaryKey="MyIniProcessingProgram"
ExeCommand="MyArgs"
Execute="deferred"
Impersonate="no"
Return="ignore"/>
And here is my InstallExecuteSequence:
<RemoveExistingProducts After="InstallValidate" />
<Custom Action="MyCA.SetProperty" Before="InstallFinalize"/>
<Custom Action="MyCA" After="MyCA.SetProperty">
CustomActionData
</Custom>
I have also tried:
Making MyCA.SetProperty conditional on UPGRADINGPRODUCTCODE, then scheduling MyCA after it--doesn't work because even if MyCA.SetProperty doesn't run, the "After" action does.
It would be best if we could avoid JavaScript/VBScript actions, or resort to checking the value of CustomActionData within the INI-updating program itself; the idea is to make the CA itself conditional, so we don't launch it unless the condition (major upgrade) is true.
Why not use this custom action:
<CustomAction
Id="MyCA"
BinaryKey="MyIniProcessingProgram"
ExeCommand="MyArgs"
Execute="deferred"
Impersonate="no"
Return="ignore"/>
with this InstallExecuteSequence?
<Custom Action="MyCA" After="InstallValidate">
UPGRADINGPRODUCTCODE
</Custom>
Please note that UPGRADINGPRODUCTCODE is set in the package which is being upgraded, so it's available only in the uninstall process of the old version.
If you want to detect an upgrade in your new version, you can use the ActionProperty column in Upgrade table:
http://msdn.microsoft.com/en-us/library/aa372379(VS.85).aspx
Basically, you need to define the upgrade rules for older and newer versions:
http://wix.sourceforge.net/manual-wix2/wix_xsd_upgradeversion.htm
This way you can detect older versions through a custom property. This property can then be used to condition your custom action.
The conditions used to gate any action (deferred included) are those of the immediate sequence. That means you could use UPGRADINGPRODUCTCODE directly as part of the condition on even your deferred action.
When the installer reaches this sequence during the immediate phase, it will evaluate the condition, and schedule the deferred execution if the condition is true. Then when it runs the deferred sequence, it will just run all the actions that were scheduled during the immediate phase.
So far, I'm NOT finding WIX to be my favorite most intuitive, easy-to-use, language/system.
I created a custom dialog, entered data, and see the values in the install log.
MSI (c) (C8:A4) [14:42:37:137]: PROPERTY CHANGE: Modifying VARRADIOBUTTONENVIRONMENT property. Its current value is 'Dev'. Its new value: 'QA'.
MSI (c) (C8:A4) [14:42:41:448]: PROPERTY CHANGE: Modifying VARTEXTSETTINGSFILENAME property. Its current value is 'C:\Path\SettingsFileGenerator.xml'. Its new value: 'Test1234.txt'.
Later, when it comes time to use the value I see this:
Action ended 14:42:43: InstallFinalize. Return value 1.
MSI (s) (BC:F8) [14:42:43:676]: Doing action: QtExec1
Action 14:42:43: QtExec1.
Action start 14:42:43: QtExec1.
MSI (s) (BC:F4) [14:42:43:682]: Invoking remote custom action. DLL: C:\Windows\Installer\MSIC4A7.tmp, Entrypoint: CAQuietExec
MSI (s) (BC!BC) [14:42:43:690]: PROPERTY CHANGE: Deleting QtExecCmdLine property. Its current value is '"[INSTALLLOCATION]XmlPreprocess.exe /i:web.config /e:[VARRADIOBUTTONENVIRONMENT] "'.
CAQuietExec: Error 0x8007007b: Command failed to execute.
CAQuietExec: Error 0x8007007b: CAQuietExec Failed
Action ended 14:42:43: QtExec1. Return value 3.
Action ended 14:42:43: INSTALL. Return value 3.
Property(S): StartIIS7ConfigTransaction = ScaConfigureIIs
And at the end when it dumps all the properties it shows this:
Property(S): VARRADIOBUTTONENVIRONMENT = QA
Property(S): VARTEXTSETTINGSFILENAME = Test1234.txt
Property(S): VerifyCurrentPropValueOfEnv = [VARRADIOBUTTONENVIRONMENT]
Here are fragments of my code. I just changed property ids to all-caps and added the secure="yes" based on another post I saw. I've tried with and without the Secure="yes".
<Property Id="VARRADIOBUTTONENVIRONMENT" Secure="yes">Dev</Property>
<Property Id="VARTEXTSETTINGSFILENAME" Secure="yes">C:\Path\SettingsFileGenerator.xml</Property>
...then later...
<Property Id="VerifyCurrentPropValueOfEnv" Value="[VARRADIOBUTTONENVIRONMENT]"/>
<Property Id="QtExecCmdLine" Value=""[INSTALLLOCATION]XmlPreprocess.exe /i:web.config /e:[VARRADIOBUTTONENVIRONMENT] ""/>
<CustomAction Id="QtExec1" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="immediate" Return="check"/>
<InstallExecuteSequence>
<Custom Action="QtExec1" After="InstallFinalize"><![CDATA[NOT(Installed)]]></Custom>
</InstallExecuteSequence>
At the end of the install, I see that XmlPreProcess.exe is installed in the installation directory.
Questions:
Is there something I'm doing wrong to get the values to substitute?
I'm using CAQuietExec because I was told it would log better errors and echo the output of the command window to the install log. I don't really care about not seeing the command window. I'm not sure how to find out what this means:
"CAQuietExec: Error 0x8007007b: Command failed to execute.". I can't tell if this is a WIX error trying to call XmlPreprocess.exe or if it got into XmlPreprocess and then threw the error.
Thanks,
Neal Walters
Looking at the log extract I think I can see the problem, you've quoted the entire command line, try this instead:
<Property Id="QtExecCmdLine" Value=""[INSTALLLOCATION]XmlPreprocess.exe" /i:"[INSTALLLOCATION]web.config" /e:[VARRADIOBUTTONENVIRONMENT]"/>