Saving product settings in registry

Sep 21, 2016 at 8:47 AM
Hi, Oleg!
I have a managed project with standart UI. Unfortunatelly, I can't use ManagedUI, because burn doesn't show UI.
After installation I want to save product settings, that user input in UI, in registry. But in SaveSettings function in session there is nothing but "INSTALLDIR".
How can I save user settings?

var project = new ManagedProject("My product",
                new Dir(@"%ProgramFiles%\My product\",
                        new Files(@"Product\*.*"))),
        new ManagedAction(CustomActions.ReadInstallDir, Return.ignore, When.Before, new Step("AppSearch"), Condition.NOT_Installed, Sequence.InstallExecuteSequence | Sequence.InstallUISequence) { Execute = Execute.firstSequence },
                new ElevatedManagedAction(CustomActions.SaveSettings, Return.check, When.After, Step.InstallFiles, Condition.NOT_Installed)));
project.UI = WUI.WixUI_InstallDir;
.......


public class CustomActions
{
[CustomAction]
public static ActionResult SaveSettings(Session session)
{
    try
    {
        Registry.LocalMachine.CreateSubKey(@"Software\" + session["ProductName"])
                                .SetValue("InstallationDirectory", session.Property("INSTALLDIR"));
    }
    catch { }

    return ActionResult.Success;
}

[CustomAction]
public static ActionResult ReadInstallDir(Session session)
{
    try
    {
        session["INSTALLDIR"] = Registry.LocalMachine.OpenSubKey(@"Software\" + session["ProductName"]) .GetValue("InstallationDirectory") .ToString();
    }
    catch { }

    return ActionResult.Success;
}
}


in SaveSettings session["ProductName"] throw exception
Coordinator
Sep 22, 2016 at 2:01 AM
Edited Sep 22, 2016 at 2:09 AM
> Unfortunatelly, I can't use ManagedUI, because burn doesn't show UI.
Yeah, this is unfortunate. WiX guys scheduled the fix for the next WiX version so let's hope...

> But in SaveSettings function in session there is nothing but "INSTALLDIR".
Yes this is because SaveSettings is a deferred custom action and MSI always initializes new completely clean disconnected session object for all deferred actions. It is one of the ugliest MSI architecture flaw. There is a hack/trick that MS offers to address the flaw. You can nominate which session properties you want to carry on from the normal session to the deferred one. It is convoluted but Wix# allows you to do it in a simple way:
//from project
project.DefaultDeferredProperties += ",<your property name>";
//from action 
new ElevatedManagedAction("OnInstall")
{
    UsesProperties = "INSTALLDIR,<your property name>"
} 
Wix# pushes INSTALLDIR to the deferred actions by default.
Read more here: https://wixsharp.codeplex.com/wikipage?title=Deployment%20scenarios#_deferred_actions

> in SaveSettings session["ProductName"] throw exception
You should use session.Property("ProductName") instead. MSI doesn't even allow to access the session object from deferred actions so you need to pull your properties from a complete different session member. And session.Property always does accessing the property transparently.

BTW you are probably better off using project.AfterInstall event. It is nothing else but an already prepared for you 'after Install' ElevatedManagedAction:
static void project_AfterInstall(SetupEventArgs e)
{
    if (e.IsInstalling)
        try
        {
            Registry.LocalMachine.CreateSubKey(@"Software\" + session.Property("ProductName"))
                                 .SetValue("InstallationDirectory", 
                                           session.Property("INSTALLDIR"));
        }
        catch{}
}
Though you still need to nominate (with project.DefaultDeferredProperties) all properties that your deferred action (event handler) is going to use.
Sep 23, 2016 at 7:05 AM
Edited Sep 23, 2016 at 9:17 AM
I tried to use UsesProperties and DefaultDeferredProperties. I change property's value in in BeforeInstall event handler, but it isn't changes
I set up UsesProperties in ElevatedManagedAction:
new Property("IP","85"),
new ElevatedManagedAction(CustomActions.SaveSettings, Return.check, When.After, Step.InstallFiles, Condition.NOT_Installed)
{
UsesProperties = "IP",
Execute = WixSharp.Execute.deferred
}

in BeforeInstall event handler I set up IP
e.Session["IP"] = "test";

in CustomActions.SaveSettings I want to get "test", but session.Property("IP") is 85



I tried to use DefaultDeferredProperties:
project.DefaultDeferredProperties += ",IP";

in BeforeInstall event handler I set up IP
e.Session["IP"] = "test";

in AfterInstall event handler session.Property("IP") is 85

What's wrong?
Coordinator
Sep 24, 2016 at 2:17 AM
MSI CustomActions scheduling can be quite tricky. Indeed BeforeInstall doesn't change the the actual property value as it is somehow became "immutable". Though Load event works OK and the changed IP value is carried to the AfterInstall correctly. If Load is not suitable for you and you need more precise CA scheduling then indeed you may have too do everything in custom actions not events.

This is the full code sample that works for your scenario:
static void Main()
{
    var project =
        new ManagedProject("ManagedSetup", 
            new Property("IP", "85"));

    project.DefaultDeferredProperties += ",IP";

    project.UI = WUI.WixUI_ProgressOnly;
    project.Load += msi_Load;
    project.AfterInstall += msi_AfterInstall;

    project.BuildMsi();
}

static void msi_Load(SetupEventArgs e)
{
    e.Session["IP"] = "test";
}

static void msi_AfterInstall(SetupEventArgs e)
{
    MessageBox.Show(e.Session.Property("IP"), "msi_AfterInstall");
}

On unrelated note. May I ask you to use code formatting in your posts. Reading non formatted code is quite difficult. I hope you understand. Thank you.
The markdown formatting rules are here: http://codeplex.codeplex.com/wikipage?title=Markdown&referringTitle=Documentation