I want to trigger some custom action by clicking some button.
I get return value 1 but the custom action did not run.
This is my log:
Action start 17:09:39: CA1.
MSI (c) (08:00) [17:09:39:220]: Invoking remote custom action. DLL: C:\Users\ARKADY~1\AppData\Local\Temp\MSI87D6.tmp, Entrypoint: CustomAction1
MSI (c) (08:EC) [17:09:39:222]: Cloaking enabled.
MSI (c) (08:EC) [17:09:39:223]: Attempting to enable all disabled privileges before calling Install on Server
MSI (c) (08:EC) [17:09:39:224]: Connected to service for CA interface.
Action ended 17:09:39: CA1. Return value 1.
My custom action is:
[CustomAction]
public static ActionResult CustomAction1(Session session)
{
session.Log("Begin CustomAction1");
return ActionResult.Success;
}
As you can see no "Begin CustomAction1" entry in the logs.
My custom action Config:
<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0" />
<supportedRuntime version="v2.0.50727"/>
</startup>
</configuration>
From wix code declaring CA:
<Binary Id="CA" SourceFile="$(var.CustomAction1.TargetDir)$(var.CustomAction1.TargetName).CA.dll" />
<CustomAction Id="CA1" BinaryKey="CA" DllEntry="CustomAction1" Execute="immediate" Return="check"/>
UI binding:
<Control Id="ManualUpdateButton" Type="PushButton" X="14" Y="188" Width="95" Height="17" Text="Manual Update">
<Publish Event="DoAction" Value="CA1" Order="1">1</Publish>
</Control>
The custom action is running, it's just not logging anything. That's a limitation in MSI -- you can't write to the log from a custom action invoked by DoAction in the UI.
Related
wxs file
<Dialog Id="InputParameters" Width="370" Height="270" Title="!(ll.MyDlg_Title)">
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="!(loc.WixUINext)" >
<Publish Event="DoAction" Value="ValidateParameters" >1</Publish>
</Control>
<Dialogue>
<CustomAction Id="ValidateParameters" BinaryKey="my_dll.CA.dll" DllEntry="ValidateParametersFunc" Execute="immediate" />
CustomAction.cs file
[CustomAction]
public static ActionResult ValidateParametersFunc(Session session)
{
session.log("perform validation");
return ActionResult.Success;
}
CustomAction.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0" />
<supportedRuntime version="v2.0.50727"/>
<configuration>
I am trying to execute a validation function when user clicks on Next Button on InputParameters Dialogue.
My all other custom actions work find which are included in InstallExecuteSequence.
If I use a VBScript to display a popup using following customAction instead of above function ValidateParametersFunc then that works fine.
<CustomAction Id="ValidateEnrollmentParameters" Script="vbscript" Execute="immediate"> <!CDATA[msgbox "You clicked?"]]></CustomAction>
But I want to execute the function ValidateParametersFunc.
I am getting error:
Action 18:58:13: ValidateParametersFunc.
Action start 18:58:13: ValidateParametersFunc.
MSI (c) (C4:20) [18:58:13:233]: Invoking remote custom action. DLL: C:\Users\MyUser\AppData\Local\Temp\2\MSI8187.tmp, Entrypoint: ValidateParametersFunc
MSI (c) (C4:A4) [18:58:13:236]: Cloaking enabled.
MSI (c) (C4:A4) [18:58:13:236]: Attempting to enable all disabled privileges before calling Install on Server
MSI (c) (C4:A4) [18:58:13:236]: Connected to service for CA interface.
Action ended 18:58:13: ValidateEnrollmentParameters. Return value 3.
MSI (c) (C4:04) [18:58:13:807]: Note: 1: 2205 2: 3: Error
MSI (c) (C4:04) [18:58:13:807]: Note: 1: 2228 2: 3: Error 4: SELECT `Message` FROM `Error` WHERE `Error` = 2896
DEBUG: Error 2896: Executing action ValidateParametersFunc failed.
The installer has encountered an unexpected error installing this package. This may indicate a problem with this package. The error code is 2896. The arguments are: ValidateParametersFunc, ,
Target framework for CustomActions project is : v4.6.1
Can someone please help?
I have tried everything given on the internet but still not able to figure out how to fix this.
I am using Wix 3.11.1 with VS2017 Extension. I set a property from a control on a custom Dialogue and then try to execute an immediate custom action. When I try to read the session, it is always empty.
As recommended I changed my action to differed execution and used an immediate action to set my property. When I run my installer I get the error: "DEBUG: Error 2896: Executing action [ActionName] failed."
In CustomDialog.wxs
<Control Id="ConnId" Type="Edit" X="60" Y="110" Height="17" Width="300" Property="CONN"/>
<Control Id="installButton" Type="PushButton" Text="Continue" Height="15" Width="60" X="240" Y="260">
<Publish Event="DoAction" Value="RegistrationInfoCustomAction">1</Publish>
<Publish Event="EndDialog" Value="Return">1</Publish>
</Control>
<Fragment>
<Binary Id="CustomActionBinary" SourceFile="..\..\CustomActions\bin\Debug\CustomActions.CA.dll"/>
<CustomAction Id="SetPropertyForShowProperty" Property="RegistrationInfoCustomAction" Execute="immediate" Value="[CONN]" Return="check" />
<CustomAction Id="RegistrationInfoCustomAction" BinaryKey="CustomActionBinary" DllEntry="SaveUserInfo" Execute="deferred" Return="check" HideTarget="no"/>
</Fragment>
In Product.wxs
<InstallExecuteSequence>
<Custom Action="SetPropertyForShowProperty" After="InstallInitialize"/>
<Custom Action="RegistrationInfoCustomAction" Before="InstallFinalize"/>
</InstallExecuteSequence>
In CustomActions.cs
[CustomAction]
public static ActionResult SaveUserInfo(Session session)
{
Debugger.Launch();
CustomActionData data = session.CustomActionData;
session.Log("Begin SaveUserInfo");
var connectionString = data["CONN"];
session.Log($"content: {connectionString}");
session.Log("End SaveUserInfo");
return ActionResult.Success;
}
The custom action works when it contains only logging statements but adding any other code make it fail. Also, the session is always empty.
In Installer Log:
MSI (c) (88:34) [16:30:21:255]: Invoking remote custom action. DLL: C:\Users\Andre\AppData\Local\Temp\MSIF1A3.tmp, Entrypoint: SaveUserInfo
MSI (c) (88:F8) [16:30:21:256]: Cloaking enabled.
MSI (c) (88:F8) [16:30:21:256]: Attempting to enable all disabled privileges before calling Install on Server
MSI (c) (88:F8) [16:30:21:256]: Connected to service for CA interface.
Action ended 16:30:41: RegistrationInfoCustomAction. Return value 3.
DEBUG: Error 2896: Executing action RegistrationInfoCustomAction failed.
The installer has encountered an unexpected error installing this package. This may indicate a problem with this package. The error code is 2896.
The arguments are: RegistrationInfoCustomAction, ,
Action ended 16:30:41: SetupDialog. Return value 3.
MSI (c) (88:8C) [16:30:41:911]: Doing action: FatalError
Similar Answers: I want to add some linkes to previous answers on the topic of deferred mode custom actions. There are links to github-samples in these answers - including one sample which uses the DTF class CustomActionData to easily send properties to deferred mode (once you are properly set up):
How to hide the value of customactiondata in logs of MSI?
Pass ConnectionString to Custom Action in WiX Installer (escape semicolon)
UPDATE: It is late, I didn't see this on first sight, but only immediate mode custom actions can be run from the setup GUI. Make a new, immediate mode custom action to set a value to your PUBLIC property CONN, and then set the value of CONN via a type 51 custom action to be assigned to the Id of the deferred mode custom action - as described below.
SecureCustomProperties: Add the property you specify to SecureCustomProperties by setting the Secure="yes" attribute:
<Property Id="MYPROPERTY" Secure="yes">Send this text to deferred mode</Property>
Name Match: the property name you assign the value to must match the deferred mode custom action Id. Looks OK in your source.
More Technical: the Property attribute's value of the type 51 action must be identical to the Id of the custom action that is consuming CustomActionData:
<!-- Declare property with value -->
<Property Id="MYPROPERTY" Secure="yes">Send this text to deferred mode</Property>
<!-- Type 51 CA: Send value of MYPROPERTY to the deferred mode CA named MyAction -->
<CustomAction Id="MyAction.SetProperty" Return="check" Property="MyAction" Value="[MYPROPERTY]" />
<!-- The deferred mode custom action -->
<CustomAction Id="MyAction" Return="check" Execute="deferred" BinaryKey="CAs" DllEntry="MyAction" />
<!-- ... -->
<!-- Inserting the CAs in sequence -->
<InstallExecuteSequence>
<Custom Action="MyAction.SetProperty" After="InstallInitialize" />
<Custom Action="MyAction" Before="InstallFinalize" />
</InstallExecuteSequence>
Here are some resources:
How to pass CustomActionData to a CustomAction using WiX?
https://www.firegiant.com/wix/tutorial/events-and-actions/at-a-later-stage/
How to access installer properties from deferred custom actions
Accessing or Setting Windows Installer Properties Through Deferred, Commit, and Rollback Custom Actions
Getting CustomActionData in deferred custom action
Just for debugging reference. And you can use: string data = session["CustomActionData"];
Tell you what, let me slide in the code to test using VBScript so you can use message boxes. Should not be necessary, just in case you have a debugging issue:
VBScript "Simple.vbs" (save as file):
MsgBox Session.Property("CustomActionData")
WiX markup change:
<Binary Id='Simple.vbs' SourceFile='Simple.vbs' />
<CustomAction Id="MyAction" Return="check" Execute="deferred" BinaryKey="Simple.vbs" VBScriptCall='' />
Just in case that is easier. Recommend you use VBScript for debugging only. I like VBScript to get a "heartbeat" when I want to eliminate all other error sources.
I have some problems with managed code custom actions. I have 3 custom actions but only one of them is working. They are called at different times in InstallExecuteSequence but moving them makes no difference. I know there not getting very far because if I place a message box at the beginning of the routine(for debugging) it never get called . Where am I going wrong ? The actions are created like so.
<Binary Id="CA" SourceFile="$(var.ca.Custom.Actions.TargetDir)$(var.ca.Custom.Actions.TargetName).CA.dll" />
<CustomAction Id="WriteRemoveArpEntry" Property="CustomActionData" Value="PNAME=$(var.ProductName)" HideTarget="yes" />
<CustomAction Id="RemoveArpEntry" BinaryKey="CA" DllEntry="RemoveProductFromARP" Return="ignore" />
<CustomAction Id="SetValueforProductFolder" Property="CustomActionData" Value="SDIR=[INSTALLDIR];TDIR=[MANUDIR]\backup\$(var.ProductName)\$(var.VersionNumber)" HideTarget="yes" />
<CustomAction Id="Backup_Product_DIR" BinaryKey="CA" DllEntry="BackupDIR" Return="ignore" />
<CustomAction Id="WriteInstallAttemptData" Property="CustomActionData" Value="PRODUCTNAME=$(var.ProductName);APPVERSION=$(var.VersionNumber)" HideTarget="yes" />
<CustomAction Id="WriteInstallAttempt" BinaryKey="CA" DllEntry="WriteXMLServer" Return="ignore" />
I then call them here
<Custom Action="SetValueforProductFolder" Before="Backup_Product_DIR">NOT Installed AND NOT REMOVE</Custom>
<Custom Action="Backup_Product_DIR" Before="InstallFinalize">NOT Installed AND NOT REMOVE</Custom>
<Custom Action="WriteRemoveArpEntry" Before="InstallFinalize">NOT Installed AND NOT REMOVE</Custom>
<Custom Action="RemoveArpEntry" After="WriteRemoveArpEntry">NOT Installed AND NOT REMOVE</Custom>
<Custom Action="WriteInstallAttemptData" After="InstallFinalize">NOT Installed AND NOT REMOVE</Custom>
<Custom Action="WriteInstallAttempt" After="WriteInstallAttemptData">NOT Installed AND NOT REMOVE</Custom>
</InstallExecuteSequence>
The headers for the routines look like this
public static ActionResult BackupDIR(Session session)
{
public static ActionResult RemoveProductFromARP(Session session)
{
Public static ActionResult WriteXMLServer(Session session)
{
However only WriteXMLServer works. In the log file I get the following .
MSI (s) (BC:9C) [07:23:45:562]: Invoking remote custom action. DLL:
C:\Windows\Installer\MSI2E2A.tmp, Entrypoint: BackupDIR CustomAction
Backup_Product_DIR returned actual error code 1154 but will be
translated to success due to continue marking
In the one that works I get
MSI (s) (BC:A0) [07:24:25:994]: Invoking remote custom action. DLL:
C:\Windows\Installer\MSICC20.tmp, Entrypoint: WriteXMLServer SFXCA:
Extracting custom action to temporary directory:
C:\Windows\Installer\MSICC20.tmp-\ SFXCA: Binding to CLR version
v4.0.30319
The answer was very simple I forgot the
[CustomAction]
For the other two routines.
I am doing WIX MSI slient installation,and also there is some custom actions checking in between if the customaction fails how to roll back or end the slient installation process of the msi??I am copying files and also starting windows service using Wix installation.When ever customaction fails how i can stop the creation of directory and starting of the windows service??
[CustomAction]
public static ActionResult PrerequisiteCheck(Session session)
{
if (Status.IsServiceAvailable && Status.IsPrinter)
{
return ActionResult.Success;
}
else
{
//Stop Installer
return ActionResult.Failure;
}
}
<CustomAction Id="CheckPrerequiste" BinaryKey="BIN_CustomAction" DllEntry="CheckPrerequiste" Return="ignore" Execute="immediate" />
<InstallExecuteSequence> <Custom Action="CA_PrerequisiteCheck" After="InstallExecute" > </Custom>
</InstallExecuteSequence>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLLOCATION" Name="Service">
<Component Id="WindowsService" Guid="AA34D3C1-26F1-4A7E-A226-1AB01501C67C">
<File Id="00690A65EA1A3EFEB7607285DBA0C8C5" KeyPath="yes" Source="WindowsService.Svc.exe" />
<ServiceInstall Id="Service" Name="Service" DisplayName="Windows Service" Description="Windows Service" Start="auto" ErrorControl="normal" Type="ownProcess" />
<ServiceControl Id="sc_Service" Name="Service" Stop="both" Remove="uninstall" Wait="yes" />
<Component Id="WindowsSvcexe" Guid="06ABF18B-DEDE-4D98-AF9F-8384539A6BC3"> <File Id="49215CE1A25380930C771772AD14519B" KeyPath="yes" Source="WindowsService.Svc.exe.config" /> </Component> </Component>
</Directory>
</Directory>
</Directory>
The installation sequence is divided into two main stages; immediate and deferred. The main reason of dividing an installation into two phases (UI and Execution) is supplying roll-back if an error occurs. All actions in the execution phase, between InstallInitialize and InstallFinalize are included in the roll-back, which is called the deferred stage. The initial phase, where the roll-back script is being prepared but the roll-back protection has not started yet is called immediate stage.
The UI sequence does not have any roll-back feature, therefore actions that alter the system should never take place there.
Custom actions that make system changes should be marked as deferred and should be scheduled to run between InstallInitialize and InstallFinalize in the execution phase. Roll-back actions should be supplied by the developer for custom actions:
…
<CustomAction Id=”myaction” Execute=”deferred” Return=”check” />
…
<InstallUISequence>
<Custom Action=”myaction” After=”CostFinalize” />
<Custom Action=”myaction2” After=”myaction” />
</InstallUISequence>
<InstallExecuteSequence>
<Custom Action=”myaction3” After=”InstallInitialize” />
<Custom Action=”myaction4” After=”myaction3” />
</InstallExecuteSequence>
Custom actions that are deferred execute during the Execute sequence’s rollback-protected phase. To give those actions rollback capabilities, separate custom actions that undo the work should be authored.
Rollback actions are scheduled before the action they are meant to revert in case of an error:
<CustomAction Id="systemChangingCA" Execute="deferred" Script="vbscript">
msgbox "Your system has been changed"
</CustomAction>
<CustomAction Id="rollbackSystemChangingCA" Execute="rollback" Script="vbscript">
msgbox "System changes are undone"
</CustomAction>
<CustomAction Id="causeError" Execute="deferred" Script="vbscript">
Err.Raise 507
</CustomAction>
These are scheduled as:
<InstallExecuteSequence>
<Custom Action="rollbackSystemChangingCA" Before="systemChangingCA" />
<Custom Action="systemChangingCA" After="InstallInitialize" />
<Custom Action="causeError" After="systemChangingCA" />
</InstallExecuteSequence>
In this example, systemChangingCA will run during deferred phase of InstallExecuteSequence. When causeError runs afterwards, it causes an exception to be thrown, which triggers a rollback. Then rollbackSystemChangingCA runs.
Wix toolset also provides its own custom action for stimulating a rollback called WixFailWhenDeferred. It is a part of WixUtilExtension and you could use it to test your rollback methods.
The folders created by WiX itself should be removed automatically during a rollback.
You can learn more about the overall concept of Microsoft Installer and WiX from the book of Nick Ramirez: "A Developer's Guide to Windows Installer XML". It has many examples and cases including the ones above.
I've managed to get WIX to launch my application on exit, but not sure how to schedule two custom actions using the WixShellExecTarget property.
One CA is to launch an app and the other is a web page based on a url from another CA. These are both launched if the appropriate checkboxes are checked.
<!-- Custom action for executing app -->
<Property Id="WixShellExecTarget" Value="[#Application.exe]" />
<CustomAction Id="LaunchApplication" BinaryKey="WixCA" DllEntry="WixShellExec" Impersonate="yes" />
<!-- Custom action for executing Webbrowser -->
<Property Id="???" Value="[CONFIGWIZARDURL]" />
<CustomAction Id="LaunchConfigWizard" BinaryKey="WixCA" DllEntry="WixShellExec" Impersonate="yes" />
Any help you could give me to get this working would be greatly appreciated.
Cheers,
Jamie
[Edit] I've tried the suggestion below and I don't get it setting the appropriate value for the second action. It doesn't go to www.google.com as I suggest:
<Publish Dialog="MyExitDialog" Control="Finish" Event="DoAction" Value="SetExec1"><![CDATA[NOT Installed]]></Publish>
<Publish Dialog="MyExitDialog" Control="Finish" Event="DoAction" Value="DoExec"><![CDATA[LAUNCHCAPTURE = "1" AND NOT Installed]]></Publish>
<Publish Dialog="MyExitDialog" Control="Finish" Event="DoAction" Value="SetExec2"><![CDATA[NOT Installed]]></Publish>
<Publish Dialog="MyExitDialog" Control="Finish" Event="DoAction" Value="DoExec"><![CDATA[LAUNCHCONFIGWIZARD = "1" AND NOT Installed]]></Publish>
Property(S): LAUNCHCONFIGWIZARD = 1
MSI (s) (5C:DC) [14:41:02:119]: PROPERTY CHANGE: Adding CONFIGWIZARDURL property. Its value is 'http://www.google.com'.
MSI (c) (DC:60) [14:41:16:166]: PROPERTY CHANGE: Adding WixShellExecTarget property. Its value is 'C:\...Application\MyApplication.exe'.
Action ended 14:41:16: SetExec1. Return value 1.
MSI (c) (DC:60) [14:41:16:181]: Doing action: DoExec
Action 14:41:16: DoExec.
Action start 14:41:16: DoExec.
MSI (c) (DC:60) [14:41:16:181]: Creating MSIHANDLE (3) of type 790542 for thread 11104
MSI (c) (DC:E0) [14:41:16:181]: Invoking remote custom action. DLL: C:\Temp\MSIA7A.tmp, Entrypoint: WixShellExec
MSI (c) (DC!8C) [14:41:16:244]: Creating MSIHANDLE (4) of type 790541 for thread 10636
MSI (c) (DC!8C) [14:41:16:244]: Creating MSIHANDLE (5) of type 790531 for thread 10636
MSI (c) (DC!8C) [14:41:16:244]: Closing MSIHANDLE (5) of type 790531 for thread 10636
MSI (c) (DC!8C) [14:41:16:447]: Closing MSIHANDLE (4) of type 790541 for thread 10636
MSI (c) (DC:E0) [14:41:16:447]: Closing MSIHANDLE (3) of type 790542 for thread 11104
Action ended 14:41:16: DoExec. Return value 1.
MSI (c) (DC:60) [14:41:16:447]: Doing action: SetExec2
Action 14:41:16: SetExec2.
Action start 14:41:16: SetExec2.
MSI (c) (DC:60) [14:41:16:447]: PROPERTY CHANGE: Deleting WixShellExecTarget property. Its current value is 'C:\...Application\MyApplication.exe'.
Action ended 14:41:16: SetExec2. Return value 1.
Action ended 14:41:16: MyExitDialog. Return value 1.
Fairly straightforward, you'll need two separate actions to "set" WixShellExecTarget - they'll just run at different times.
First, you'll setup the actions that are going to be run.
<CustomAction Id="SetExec1" Property="WixShellExecTarget" Value="[#Application.exe]" />
<CustomAction Id="SetExec2" Property="WixShellExecTarget" Value="[CONFIGWIZARDURL]" />
<CustomAction Id="DoExec" BinaryKey="WixCA" DllEntry="WixShellExec" Impersonate="yes" Return="ignore" />
Now you want to schedule those actions to actually run, in this example I'm tying all of the actions to the Finish button on the final installation dialog. As we're publishing to a Control element, WiX will automatically set Publish/#Order to one greater than the previous event.
In addition, all these actions are conditioned to only execute during installation as this same dialog is displayed during removal and repair.
You'll likely also want to condition these based on the status of your checkboxes if execution is optional.
<UI>
<!-- Publish set/do for first action -->
<Publish Dialog="ExitDialog" Control="Finish" Event="DoAction" Value="SetExec1">
<![CDATA[NOT Installed]]>
</Publish>
<Publish Dialog="ExitDialog" Control="Finish" Event="DoAction" Value="DoExec">
<![CDATA[NOT Installed]]>
</Publish>
<!-- Publish set/do for second action -->
<Publish Dialog="ExitDialog" Control="Finish" Event="DoAction" Value="SetExec2">
<![CDATA[NOT Installed]]>
</Publish>
<Publish Dialog="ExitDialog" Control="Finish" Event="DoAction" Value="DoExec">
<![CDATA[NOT Installed]]>
</Publish>
</UI>