Search code examples
wixwindows-installerwix3.11

Wix Custom Action - session empty and error on deferred action


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


Solution

  • 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):


    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:


    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.