27

I have an MSI file being created with Wxs 3.0. My MSI references a C# custom action, written using the new C# Custom Action project.

I want to pass an argument to msiexec that gets routed to my custom action - for example:

msiexec /i MyApp.msi ENVIRONMENT=TEST#

In my .wxs file, I refer to my custom action like this:

<Property Id="ENVIRONMENT"/>
<Binary Id="WixCustomAction.dll"  SourceFile="$(var.WixCustomAction.Path)" />
<CustomAction Id="WixCustomAction" BinaryKey="WixCustomAction.dll"    DllEntry="ConfigureSettings"/>
<InstallExecuteSequence>
   <Custom Action="WixCustomAction" After="InstallFiles"></Custom>
</InstallExecuteSequence>

My C# custom action is set up like this:

[CustomAction]
public static ActionResult ConfigureSettings(Session session)
{

}

I was expecting to be able to access the property like this:

string environmentName = session.Property["ENVIRONMENT"];

but this doesn't seem to work.

How do I access the property I passed to msiexec in my custom action?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
David Laing
  • 7,605
  • 10
  • 33
  • 44

5 Answers5

30

If instead of

<CustomAction Id="SetCustomActionDataValue"
              Return="check"
              Property="Itp.Configurator.WixCustomAction"
              Value="[ENVIRONMENT],G2,[CONFIGFILE],[TARGETDIR]ITP_v$(var.VERSION_MAJOR)" />

you write this:

<CustomAction Id="SetCustomActionDataValue"
              Return="check"
              Property="Itp.Configurator.WixCustomAction"
              Value="Environment=[ENVIRONMENT];G=G2;ConfigFile=[CONFIGFILE];TargetDir=[TARGETDIR]ITP_v$(var.VERSION_MAJOR)" />

then you will be able to reference your variables like this:

string env=session.CustomActionData["Environment"];
liwp
  • 6,746
  • 1
  • 27
  • 39
Tomasz Grobelny
  • 2,666
  • 3
  • 33
  • 43
14

Just for completeness; using the method described by Jeremy Lew, in the blog above allows for the following:

Calling:

msiexec /i ITP.Platform.2.msi ENVIRONMENT=QA CONFIGFILE=EnvironmentConfig.xml

With this in the .wxs file:

<Property Id="ENVIRONMENT" Secure="yes" />
<Property Id="CONFIGFILE" Secure="yes" />
<Binary Id="Itp.Configurator.WixCustomAction.dll"
        SourceFile="$(var.Itp.Configurator.WixCustomAction.Path)" />

<CustomAction Id="SetCustomActionDataValue"
              Return="check"
              Property="Itp.Configurator.WixCustomAction"
              Value="[ENVIRONMENT],G2,[CONFIGFILE],[TARGETDIR]ITP_v$(var.VERSION_MAJOR)" />

<CustomAction Id="Itp.Configurator.WixCustomAction"
              Return="check"
              Execute="deferred"
              BinaryKey="Itp.Configurator.WixCustomAction.dll"
              DllEntry="ConfigureItpBrandSettings" />

<InstallExecuteSequence>
  <Custom Action="SetCustomActionDataValue" After="InstallFiles"></Custom>
  <Custom Action="Itp.Configurator.WixCustomAction" After="SetCustomActionDataValue"></Custom>
</InstallExecuteSequence>

With a custom action:

    /// <summary>
    /// CustomAction keys should be Environment,BrandId,ConfigPath,itpBasePath
    /// </summary>
    /// <param name="session"></param>
    /// <returns></returns>
    [CustomAction]
    public static ActionResult ConfigureItpBrandSettings(Session session)
    {
        string[] arguments = GetCustomActionDataArguments(session);

        string environmentName = arguments[0];
        string brandId = arguments[1];
        string configPath = arguments[2];
        string itpBasePath = arguments[3];

        //Do stuff

        return ActionResult.Success;
    }

    private static string[] GetCustomActionDataArguments(Session session)
    {
        string[] keys = new string[session.CustomActionData.Keys.Count];
        session.CustomActionData.Keys.CopyTo(keys,0);
        return keys[0].Split(',');
    }

works.

Parsing the CustomActionData arguments is pretty ugly, but it does work. Hopefully someone knows a more elegant way to do this.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
David Laing
  • 7,605
  • 10
  • 33
  • 44
8

Your custom action needs to be a deferred custom action in order to run after InstallFiles. Deferred custom actions do not have access to properties, but they do have access to CustomActionData. See this blog post for a discussion on how to get what to do about it. (This example is a VBScript custom action, but you will be able to retrieve the value through the session.CustomActionData collection.)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
jlew
  • 10,491
  • 1
  • 35
  • 58
  • 2
    Note the link to the blog post doesn't work anymore. The new link is http://blogs.claritycon.com/sajojacob/2008/02/29/customactiondata-in-wix-with-deferred-custom-actions/ – Sébastien Nussbaumer Apr 29 '11 at 07:54
  • 3
    @SébastienNussbaumer: well, that link is now dead, too... jlew: you should post actual code here, as opposed to links. – scrat.squirrel Apr 05 '13 at 13:23
8

Here is my working code:

<Binary Id="MyCA" SourceFile="..\bin\ChainerRun.CA.exe" />

<CustomAction Id="SetCustomActionDataValue" Return="check" Property="CustomActionData" Value="TARGETDIR=[TARGETDIR];AA=Description;" />

<CustomAction Id="ReadAndSet" 
            BinaryKey="MyCA" 
            DllEntry="ReadAndSet" 
            Execute="immediate"
            HideTarget="no" 
            Return="check" />

<InstallExecuteSequence>
    <Custom Action="SetCustomActionDataValue" Before="InstallFiles" />
    <Custom Action="ReadAndSet" After="SetCustomActionDataValue" />
</InstallExecuteSequence>

In the C# custom action function:

[CustomAction]
public static ActionResult ReadAndSet(Session session)
{
    ActionResult retCode = ActionResult.NotExecuted;

    System.Diagnostics.Debug.Assert(false);

    session.Log("ReadAndSet() begins ...");

    string installLocation = session.CustomActionData["TARGETDIR"];
    string hostName = session.CustomActionData["AA"];
    ...
}
Dave Andersen
  • 5,337
  • 3
  • 30
  • 29
Ben
  • 181
  • 3
  • 1
0

If we're talking about Wix Sharp (and not plain Wix with its XML stuff), adding a custom property is a piece of cake. All you have to do is to set UsesProperties property for your managed action.

For example, if you want to add a custom property named "MYPROP", just define your action like this:

new ElevatedManagedAction(nameof(CustomActions.MyCustomAction))
{
    Condition = Condition.Installed,
    When = When.Before,
    Step = Step.RemoveFiles,
    Return = Return.check,
    Execute = Execute.deferred,
    UsesProperties = "MYPROP"
}

Set the property value via msiexec command line:

msiexec /i my.msi MYPROP=MYVALUE

And then you'll be able to access it from your custom action:

[CustomAction]
public static ActionResult MyCustomAction(Session session)
{
    session.Log("MYPROP VALUE: " + session.CustomActionData["MYPROP"]);
    return ActionResult.Success;
}

When property is not set via command line the default value will be an empty string.

Funbit
  • 1,591
  • 2
  • 14
  • 15