WiX CustomAction fires in UI but not Execute Sequence - wix

We need our MSI to store the password in the registry, so users can use our installer to change connection strings, etc.
We found MsiExt for its cryptography DLL and are trying to configure the custom actions.
Our users will need to be able to install both through the UI dialogs and silently -- so we need to able to execute in both sequences.
Here is a sample of our Product.wxs
<Property Id="DB_PASSWORD" Secure="yes"/>
<Property Id="P.DB_PASSWORD">
<RegistrySearch Id="S.DB_PASSWORD" Root="HKLM" Key="SOFTWARE\$(var.Manufacturer)\$(var.ProductName)" Name="DB_PASSWORD" Type="raw" Win64="$(var.Win64)"/>
</Property>
<Component Id="c.RegistryEntries" Guid="XXXXX-XXXXX-XXXXX-XXXXX" Directory="INSTALLDIR">
<RegistryKey Root="HKLM" Key="SOFTWARE\$(var.Manufacturer)\$(var.ProductName)" Action="createAndRemoveOnUninstall">
<RegistryValue Id="R.DB_PASSWORD" Name="DB_PASSWORD" Value="[ENCRYPTED_DBPASSWORD]" Type="string" />
</RegistryKey>
</Component>
<!--For encrypting the database password on the way to the registry-->
<Binary Id="Cryptography" SourceFile="..\..\lib\msiext-1.4\CustomActions\Cryptography.dll"/>
<!--This property will receive the encrypted DB_PASSWORD that the user enters and will be encrypted-->
<Property Id="CRYPTPROTECT_DATA" Hidden="yes" />
<Property Id="CRYPTPROTECT_FLAGS" Value="CRYPTPROTECT_LOCAL_MACHINE|CRYPTPROTECT_UI_FORBIDDEN" />
<CustomAction Id="EncryptPassword" BinaryKey="Cryptography" DllEntry="CryptProtectDataHex" Execute="immediate" />
<CustomAction Id="SetDBUSERsPASSWORDForEncryption" Property="CRYPTPROTECT_DATA" Value="[DB_PASSWORD]" />
<Property Id="ENCRYPTED_DBPASSWORD" Hidden="yes" />
<SetProperty Id="ENCRYPTED_DBPASSWORD" Value="[CRYPTPROTECT_RESULT]" Sequence="execute" After="SetDBUSERsPASSWORDForEncryption" />
<!--This is for decrypting the registry value-->
<Property Id="CRYPTUNPROTECT_DATA" Hidden="yes" />
<Property Id="CRYPTUNPROTECT_FLAGS" Value="CRYPTPROTECT_LOCAL_MACHINE|CRYPTPROTECT_UI_FORBIDDEN" />
<SetProperty Id="CRYPTUNPROTECT_DATA" Value="[P.DB_PASSWORD]" Before="DecryptPassword" >NOT(DB_PASSWORD)</SetProperty>
<CustomAction Id="DecryptPassword" BinaryKey="Cryptography" DllEntry="CryptUnprotectDataHex" Execute="firstSequence" />
<CustomAction Id="SetDBUSERsDecryptedPASSWORD" Property="DB_PASSWORD" Value="[CRYPTUNPROTECT_RESULT]"/>
<CustomAction Id="LaunchApplication" BinaryKey="WixCA" DllEntry="WixShellExec" Impersonate="yes" />
<InstallUISequence>
<Custom Action="DecryptPassword" After="CostFinalize"><![CDATA[NOT(DB_PASSWORD)]]></Custom>
<Custom Action="SetDBUSERsDecryptedPASSWORD" After="DecryptPassword" ><![CDATA[NOT(DB_PASSWORD)]]></Custom>
</InstallUISequence>
<InstallExecuteSequence>
<Custom Action="SchedXmlConfig" After="InstallFiles"><![CDATA[(NOT REMOVE~="All")]]></Custom>
<Custom Action="SetDBUSERsPASSWORDForEncryption" Before="InstallInitialize" />
<Custom Action="EncryptPassword" After="SetDBUSERsPASSWORDForEncryption" />
<Custom Action="DecryptPassword" Before="InstallFiles"><![CDATA[CRYPTUNPROTECT_RESULT]]></Custom>
<Custom Action="SetDBUSERsDecryptedPASSWORD" After="DecryptPassword" ><![CDATA[CRYPTUNPROTECT_RESULT]]></Custom>
</InstallExecuteSequence>
The install log shows that the DecryptPassword is firing during the UISequence. The decryption custom action fires, then the DecryptPassword works.
Action ended 17:52:43: CostFinalize. Return value 1.
MSI (c) (CC:A8) [17:52:43:936]: Doing action: SetCRYPTUNPROTECT_DATA
Action 17:52:43: SetCRYPTUNPROTECT_DATA.
Action start 17:52:43: SetCRYPTUNPROTECT_DATA.
MSI (c) (CC:A8) [17:52:43:937]: PROPERTY CHANGE: Adding CRYPTUNPROTECT_DATA property. Its value is '**********'.
Action ended 17:52:43: SetCRYPTUNPROTECT_DATA. Return value 1.
MSI (c) (CC:A8) [17:52:43:937]: Doing action: DecryptPassword
Action 17:52:43: DecryptPassword.
Action start 17:52:43: DecryptPassword.
MSI (c) (CC:7C) [17:52:49:129]: Invoking remote custom action. DLL: C:\Users\kujotx\AppData\Local\Temp\MSI9904.tmp, Entrypoint: CryptUnprotectDataHex
MSI (c) (CC:54) [17:52:49:130]: Cloaking enabled.
MSI (c) (CC:54) [17:52:49:130]: Attempting to enable all disabled privileges before calling Install on Server
MSI (c) (CC:54) [17:52:49:130]: Connected to service for CA interface.
CryptUnprotectDataHex: MSI Extensions 1.4.1114.0
MSI (c) (CC!94) [17:52:49:236]: PROPERTY CHANGE: Adding CRYPTUNPROTECT_RESULT property. Its value is 'password'.
Action ended 17:52:49: DecryptPassword. Return value 1.
MSI (c) (CC:A8) [17:52:49:238]: Doing action: SetDBUSERsDecryptedPASSWORD
Action 17:52:49: SetDBUSERsDecryptedPASSWORD.
Action start 17:52:49: SetDBUSERsDecryptedPASSWORD.
MSI (c) (CC:A8) [17:52:49:239]: PROPERTY CHANGE: Adding DB_PASSWORD property. Its value is '**********'.
Action ended 17:52:49: SetDBUSERsDecryptedPASSWORD. Return value 1.
My problem is that SetCRYPTUNPROTECT_DATA is not executing during ExecuteSequence, so DecryptPassword fails:
Action ended 17:53:47: AppSearch. Return value 1.
MSI (s) (28:1C) [17:53:47:206]: Doing action: DecryptPassword
Action 17:53:47: DecryptPassword.
Action start 17:53:47: DecryptPassword.
MSI (s) (28:98) [17:53:47:217]: Invoking remote custom action. DLL: C:\Windows\Installer\MSI9020.tmp, Entrypoint: CryptUnprotectDataHex
CryptUnprotectDataHex: MSI Extensions 1.4.1114.0
CryptUnprotectDataHex: [CryptUnprotectDataHex] std::exception: 0x80070057 - Error in CryptUnprotectData: The parameter is incorrect.
MSI (s) (28!DC) [17:53:47:237]: PROPERTY CHANGE: Adding CA_ERROR property. Its value is '0x80070057 - Error in CryptUnprotectData: The parameter is incorrect.'.
CustomAction DecryptPassword returned actual error code 1603 (note this may not be 100% accurate if translation happened inside sandbox)
Action ended 17:53:47: DecryptPassword. Return value 3.
Action ended 17:53:47: INSTALL. Return value 3.
Can you point out how to schedule our custom actions to get this to decrypt properly?

Your problem is likely due to one of the following:
The <SetProperty> element that sets the CRYPTUNPROTECT_DATA property is being conditionally run based on the value of DB_PASSWORD not being set, but we can't see from the logs that this property is indeed not set. The property action won't run if this evaluates to false.
The default setting for the Sequence attribute on the <SetProperty> element is not working correctly. Try setting the Sequence attribute to first, which will schedule it to be run in the first of either InstallUISequence or InstallExecuteSequence (will be first in silent mode). If that doesn't work, try setting it to both explicitly.
Try changing the Before attribute on the custom action for DecryptPassword in the InstallExecuteSequence to After="InstallInitialize", which is much earlier on in the install sequence than InstallFiles. This will get you behavior that is similar to running with the UI Sequence, which computes and modifies your properties and runs your actions prior to anything actually being installed.
See this page for suggested sequencing (and see relative ordering of events)

Related

How to hide the value of customactiondata in logs of MSI?

I have a deferred custom action which fetches a property using Customactiondata, it contains the value of password that should not be displayed in the log .
Packaging tool used: WIX
Custom action written in C++
I have tried the below workarounds nothing seems to be working.
Marked the property and CA name as hidden
Hidetarget = yes in CA definition
what needs to be done?
Code:
<CustomAction Id="CASETLOGINFORRCSERVICES" Return="check" HideTarget="yes" Execute="deferred" Impersonate="no" TerminalServerAware="no" DllEntry="SetLoginForRCServices" BinaryKey="CA_Dll" />
log:
MSI (s) (7C:CC) [18:35:39:011]: Executing op: CustomActionSchedule(Action=CASETLOGINFORRCSERVICES,ActionType=3073,Source=BinaryData,Target=SetLoginForRCServices,CustomActionData=Deps###151232323)
MSI (s) (7C:B0) [18:35:39:038]: Invoking remote custom action. DLL: C:\WINDOWS\Installer\MSIEB69.tmp, Entrypoint: SetLoginForRCServices
MsiHiddenProperties: There is a property you can set to hide property values from being written to the log: MsiHiddenProperties property (there are further links in there to more information on preventing confidential information in your MSI).
Custom Action: Setting the attribute HideTarget="yes" for the custom action will set the above property value for you. However this feature does not seem to hide any value you hard-code in the property table from the log - so if you set an actual value for the property in the property table you need to set the property itself hidden as well (you can set a value programmatically or via the GUI without setting it in the property table). Here are samples:
HideTarget="Yes":
<CustomAction Id="ReadProperyDeferred" HideTarget="yes" ... />
Property Hidden="yes":
<Property Id="MYPROPERTY" Hidden="yes" Secure="yes">Text</Property>
Samples: Sample WiX source here: https://github.com/glytzhkof/WiXDeferredModeSample.
Here is another sample for deferred mode - it uses the DTF class CustomActionData to easily send properties to deferred mode: https://github.com/glytzhkof/WiXDeferredModeSampleDTF
Remember to avoid custom actions if you can: Why is it a good idea to limit the use of custom actions in my WiX / MSI setups?
Sensitive Information: Here is an answer on preventing sensitive or unwanted information to make it into your MSI: How do I avoid distributing sensitive information in my MSI by accident?
Code Extract: Prefer to open the above sample. However, here is a "compressed" sequence of WiX constructs needed for deferred mode custom actions retrieving data from a set-property custom action:
<Property Id="MYPROPERTY" Hidden="yes" Secure="yes">Send this text to deferred mode</Property>
<Binary Id="CustomActions" SourceFile="$(var.CustomActionSample.TargetDir)$(var.CustomActionSample.TargetName).CA.dll" />
<CustomAction Id="SetProperty" Return="check" Property="ReadProperyDeferred" Value="[MYPROPERTY]" />
<CustomAction Id="ReadProperyDeferred" HideTarget="yes" BinaryKey="CustomActions" Execute="deferred" DllEntry="TestCustomAction" />
<InstallExecuteSequence>
<Custom Action='SetProperty' Before='InstallInitialize'></Custom>
<Custom Action='ReadProperyDeferred' Before='InstallFinalize'></Custom>
</InstallExecuteSequence>
Links:
WIX execute custom action with admin privilege
Older answer of mine on deferred mode
How can I automate testing an MSI is installable with UAC?
Add HideTarget="Yes" to the custom action.

Cannot run default browser from WIX installation

From tutorial:
<Property Id="BROWSER">
<RegistrySearch Id='DefaultBrowser' Type='raw' Root='HKCR' Key='http\shell\open\command' />
</Property>
<CustomAction Id='LaunchBrowser' Property='BROWSER' ExeCommand='www.something.com' Return='asyncNoWait' />
<InstallExecuteSequence>
...
<Custom Action='LaunchBrowser' After='InstallFinalize'>NOT Installed</Custom>
</InstallExecuteSequence>
As I can see from installation traces, property BROWSER is calculated correctly:
Property(S): BROWSER = "C:\Program Files (x86)\Internet
Explorer\iexplore.exe" -nohome
But browser is not open after installation.
MSI (s) (88:90) [18:38:30:331]: Doing action: LaunchBrowser MSI (s)
(88:90) [18:38:30:331]: Note: 1: 2205 2: 3: ActionText Action start
18:38:30: LaunchBrowser. Action ended 18:38:30: INSTALL. Return value
1. Action ended 18:38:30: LaunchBrowser. Return value 1631.
...
MSI (c) (64:6C) [18:38:30:409]: Product: WebPrintingService -- Installation completed successfully.
What could be wrong?
Windows 7.
UAC - default.
UPDATE:
As a workaround it is possible to use following code(but I am not sure that it is a good workaround):
<Property Id="BROWSER">
<RegistrySearch Id='DefaultBrowser' Type='raw' Root='HKCR' Key='http\shell\open\command' />
</Property>
<CustomAction Id="LaunchBrowser" Directory="INSTALLDIR" Impersonate="no" Execute="deferred" ExeCommand='[BROWSER] "test.html"' Return="check"/>
<InstallExecuteSequence>
<Custom Action='LaunchBrowser' Before='InstallFinalize'>NOT Installed</Custom>
</InstallExecuteSequence>
I have not made an installer that can run the default browser after installtion but I have made an installer with WiX that launches the installed application. As far as I know, the ExeCommand property must contain the file path to an executable file. It can be either the path on your computer directly or a string property you defined containing the file path. Maybe you can tell the program wich file it should open, wich would be in your case a website.
I hope this helps.

How to execute the custom action in silent mode in wix?

I am trying to execute the custom action at the time of uninstall the installer in wix.It is working perfectly but it is showing the splash screen of cmd prompt at the time of custom action.Latter I tried with CAQuietExec but it is unable to uininstall the installer and giving error.
(CAQuietExec: Error 0x80070057: failed to get command line data).
The command that I am using is :
<Fragment>
<Property Id="ModifyOutlookRegInitSign_14" Value=""[SystemFolder]reg.exe" ADD "HKCU\SOFTWARE\Microsoft\Office\14.0\Outlook\Security" /v InitSign /t REG_DWORD /d 0 /f"/>
<CustomAction Id="ModifyOutlookRegInitSign_14" BinaryKey="WixCA" DllEntry="CAQuietExec"
Execute="deferred" Return="check" />
<InstallExecuteSequence>
<Custom Action="ModifyOutlookRegInitSign_14" Before="InstallFinalize"></Custom>
</InstallExecuteSequence>
</Fragment>
If it is an immediate custom action, the name of the property containing the command line as value must have an Id="QtExecCmdLine". For other types of custom actions read Quiet Execution Custom Action.
It seems to me that you are trying to update HKCU during the uninstall. This is probably because Windows Installer doesn't natively support the ability to do so.
But your proposed solution is lacking in several way. Mainly that it doesn't support rollback and doesn't support cleaning up other user profiles.
Did this registry entry had to be implemented in HKCU? Could it be implemented in HKLM?
I've created a custom action to kill a process silently like this:
<!-- WixQuietExecCmdLine specify the cmd to be executed -->
<Property Id="WixQuietExecCmdLine" Value='"[WindowsFolder]System32\TaskKill.exe" /F /T /IM MyApp.exe'/>
<!-- From WiX v3.10, use WixQuietExec -->
<CustomAction Id="MyAppTaskKill" BinaryKey="WixCA" DllEntry="WixQuietExec" Execute="immediate" Return="ignore"/>
<!-- trigger the custom action -->
<InstallExecuteSequence>
<Custom Action='MyAppTaskKill' Before='InstallValidate'></Custom>
</InstallExecuteSequence>
You have more info about the possible configuration combinations here:
http://wixtoolset.org/documentation/manual/v3/customactions/qtexec.html
Wrap your custom action around a Property with Id set to WixQuietExecCmd.
<Property Id="WixQuietExecCmdLine" Value="command line to run"/>
WiX Property Element
WiX Quiet Execution of Custom Action

"Installed" condition is always true when installing or uninstalling in WiX

I have a WiX installer which does some work, and at the end, launch RSYNC installer (it's an EXE file, no problem to have both working at same time).
But when uninstalling, I want to remove RSYNC too.
<CustomAction Id="InstallRSyncDaemon" FileKey="cwRsyncServer_4.0.5_Installer.exe" ExeCommand="/q" Execute="deferred" Return="ignore" Impersonate="no" />
<CustomAction Id="UninstallRSyncDaemon" FileKey="rsync_uninstall.bat" ExeCommand="[INSTALLDIR]" Execute="deferred" Return="check" Impersonate="no" />
<CustomAction Id="EditConf" FileKey="rsync.bat" ExeCommand="[INSTALLDIR]" Execute="deferred" Return="ignore" Impersonate="no"/>
<InstallExecuteSequence>
<Custom Action='InstallRSyncDaemon' Before='InstallFinalize'>(NOT remove="ALL")</Custom>
<Custom Action='EditConf' After='InstallRSyncDaemon'>(NOT remove="ALL")</Custom>
<Custom Action='UninstallRSyncDaemon' Before='RemoveFiles'>remove="ALL"</Custom>
</InstallExecuteSequence>
My problem is that during uninstalling, the Custom Action 'UninstallRSyncDaemon' is skipped:
MSI (s) (58:78) [17:37:40:475]: Skipping action: UninstallRSyncDaemon (condition is false)
I have tried with "installed" and other conditions, with the same result.
At the beginnning of the log file, I have:
MSI (c) (9C:80) [17:37:36:772]: PROPERTY CHANGE: Adding ACTION property. Its value is 'INSTALL'.
MSI (c) (9C:80) [17:37:36:772]: Doing action: INSTALL
Action 17:37:36: INSTALL.
Action start 17:37:36: INSTALL.
MSI (c) (9C:80) [17:37:36:772]: UI Sequence table 'InstallUISequence' is present and populated.
MSI (c) (9C:80) [17:37:36:772]: Running UISequence
MSI (c) (9C:80) [17:37:36:772]: PROPERTY CHANGE: Adding EXECUTEACTION property. Its value is 'INSTALL'.
If I try to change to the condition to launch it during installation, it is working, so for me it is just the statement of the condition.
But I do not understand why, this is the condition always used in a tutorial or in an answer on Stack Overflow.
I think the remove should be in upper case. Try this.
REMOVE="ALL"
But it will run in modify and Upgrade also. Check more about this condition here.

Attach Registry Property with Edit Control in WiX

I'm looking to update the value of registry using a Edit Control in WiX installation process.
I have no problem in using Edit Control which I'm using as follows -
<Property Id="WIXUI_USERLIST" Value="Demo;" Secure="yes"/>
<Control Id="UsersList" Type="Edit" X="105" Y="192" Width="180" Height="18" Property="WIXUI_USERLIST" Indirect="no" />
This works fine to take user input and push the value in registry using WIXUI_USERLIST.
Problem start when I introduce following code to read the registry to display existing value in Edit control in a property and attach it to WIXUI_USERLIST as
<Property Id="USERLIST" Secure="yes">
<RegistrySearch Id="UserList"
Root="HKLM"
Key="[APPLICATIONHIVE]"
Name="UserList"
Type="raw"
Win64="yes" />
</Property>
<SetProperty Id="WIXUI_USERLIST" Value="[USERLIST]" After="AppSearch">USERLIST</SetProperty>
Looking at the log suggest the value correctly passed to the INSTALL phase but gets overwritten by USERLIST.
Action 14:37:22: INSTALL.
Action start 14:37:22: INSTALL.
MSI (s) (78:44) [14:37:22:770]: Running ExecuteSequence
MSI (s) (78:44) [14:37:22:770]: Doing action: FindRelatedProducts
MSI (s) (78:44) [14:37:22:770]: Note: 1: 2205 2: 3: ActionText
Action 14:37:22: FindRelatedProducts. Searching for related applications
Action start 14:37:22: FindRelatedProducts.
MSI (s) (78:44) [14:37:22:772]: Skipping FindRelatedProducts action: already done on client side
Action ended 14:37:22: FindRelatedProducts. Return value 0.
MSI (s) (78:44) [14:37:22:772]: Doing action: AppSearch
MSI (s) (78:44) [14:37:22:772]: Note: 1: 2205 2: 3: ActionText
Action 14:37:22: AppSearch. Searching for installed applications
Action start 14:37:22: AppSearch.
MSI (s) (78:44) [14:37:22:774]: Skipping AppSearch action: already done on client side
Action ended 14:37:22: AppSearch. Return value 0.
MSI (s) (78:44) [14:37:22:774]: Doing action: WIXUI_USERLIST
MSI (s) (78:44) [14:37:22:774]: Note: 1: 2205 2: 3: ActionText
Action 14:37:22: WIXUI_USERLIST.
Action start 14:37:22: WIXUI_USERLIST
MSI (s) (78:44) [14:37:22:775]: PROPERTY CHANGE: Modifying WIXUI_USERLIST property. Its current value is 'Gurinder;TestUser'. Its new value: 'Gurinder'.
Action ended 14:37:22: SetWIXUI_PORTSERVERADD. Return value 1..
In log "Gurinder" is stored in registry and edit during install to "Gurinder;TestUser"
There are a few ways to solve this problem but I believe the following is the probably the most efficient. It just requires a few tweaks to what you've done already.
First, I'd put the default values in USERLIST so that WIXUI_USERLIST can always be set. Second, need the WIXUI_USERLIST to be set only once in the UI or Execute sequence. The SetProperty element doesn't expose this ability but we can get to it using the CustomAction element. Getting the action to run only once in the sequences is the magic.
The resulting code for setting the properties would go a little like this:
<Property Id="USERLIST" Value="Demo;" Secure="yes">
<RegistrySearch Id="UserList"
Root="HKLM"
Key="[APPLICATIONHIVE]"
Name="UserList"
Type="raw"
Win64="yes" />
</Property>
<!-- replaces SetProperty but adds the ability to run only in the first sequence -->
<CustomAction Id='SetWIXUI_USERLIST' Property='WIXUI_USERLIST' Value='[USERLIST]'
Execute='firstSequence'>
<InstallUISequence>
<Custom Action='SetWIXUI_USERLIST' After='AppSearch' />
</InstallUISequence>
<InstallExecuteSequence>
<Custom Action='SetWIXUI_USERLIST' After='AppSearch' />
</InstallExecuteSequence>
Update: Additionally, I went and double checked my memory against the MSI SDK. The AppSearch action is only executed once. That means I believe you could probably remove all the WIXUI_USERLIST and custom actions and stuff and just use USERLIST everywhere. So an even simpler solution is to remove all of this and replace all instances of your WIXUI_USERLIST with USERLIST. :)