66

How are properties set on CustomActionData to be retrieved by a deferred custom action?

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Ayo I
  • 6,642
  • 4
  • 24
  • 37
  • [Github-sample of a deferred mode C# custom action](https://github.com/glytzhkof/WiXDeferredModeSample). From [this answer](https://stackoverflow.com/a/65366057/129130). And the [basic WiX constructs summarized here](https://stackoverflow.com/a/54298965/129130). – Stein Åsmul Dec 19 '20 at 02:38

3 Answers3

142

Deferred custom actions can not directly access installer properties (reference). In fact, only CustomActionData property

session.CustomActionData

and other methods and properties listed here are available on the session object.

Therefore, for a deferred custom action to retrieve a property such as the INSTALLLOCATION, you have to use a type 51 custom action — i.e. a set-property custom action — to pass that information along and you'll consume the data from the CustomAction's C# code through session.CustomActionData. (see reference & reference)

Below is an example of a type 51 custom action (CustomAction1) that will set a property that can be retrieved in CustomAction2.

<CustomAction Id="CustomAction1"
              Property="CustomAction2"
              Value="SomeCustomActionDataKey=[INSTALLLOCATION]"
/>

Notice that Property attribute name is CustomAction2. This is important. The Property attribute's value of the type 51 action must be equal/identical to the name of the custom action that is consuming CustomActionData. (see reference)

Notice the name SomeCustomActionDataKey in the Value attribute key/value pair? In your C# code in the consuming custom action (CustomAction2), you'll look-up that property from CustomActionData by using the following expression:

string somedata = session.CustomActionData["SomeCustomActionDataKey"];

The key that you use to retrieve the value from CustomActionData is NOT the value in Property attribute of the type 51 custom action, but the key from the key=value pair in the Value attribute. (Important takeaway: CustomActionData is populated by setting an installer property that has the same name as the Id of the consuming custom action, but CustomActionData keys are NOT installer properties.) (see reference)

In our scenario the consuming custom action is a deferred custom action defined somewhat like the below:

<Binary Id="SomeIdForYourBinary" SourceFile="SomePathToYourDll" />
<CustomAction Id="CustomAction2"
              BinaryKey="SomeIdForYourBinary"
              DllEntry="YourCustomActionMethodName"
              Execute="deferred"
              Return="check"
              HideTarget="no"
/>

Configuring the InstallExecuteSequence

Of course, the consuming custom action (CustomAction2) must run after the type 51 custom action (CustomAction1). So you'll have to schedule them like this:

<InstallExecuteSequence>
  <!--Schedule the execution of the custom actions in the install sequence.-->
  <Custom Action="CustomAction1" Before="CustomAction2" />
  <Custom Action="CustomAction2" After="[SomeInstallerAction]" />      
</InstallExecuteSequence>
Community
  • 1
  • 1
Ayo I
  • 6,642
  • 4
  • 24
  • 37
  • 5
    Alexey, thanks for taking the time to write this up. This has been a really big help to me. Unfortunately I can only give you +1... – Tim Long Jan 17 '13 at 20:05
  • As @TimLong said, a huge thank you. Wish I could +1 this many, many times. – Lynn Crumbling Nov 19 '13 at 22:28
  • Did not understand it until I actually ran it. Then I saw the logic. Way to little GOOD readable info on this topic. Thanks! – Wolf5 Jun 09 '14 at 16:43
  • 11
    If you need to pass more then one parameter, you cannot add another CustomAction, they overwrite, but you should set your parameters separating them with a semicolon: `Value="ActionDataKey1=Value1;ActionDataKey2=Value2"`. – Marco Ciambrone Sep 01 '14 at 10:57
  • 1
    You don't have to follow the key-value-pair format, you can provide _any_ string value and read it with `session.CustomActionData.ToString()`. – rustyx Mar 07 '15 at 22:31
  • I stumbled across this question whilst looking for a solution to write settings to appsettings.Json file after a ASP.NET MVC Core installation. I wrote a CustomAction to do it, but it was stumbling over when it tried to write to settings file during installation because of permission issues. The only way to do it was to defer the execution but I had the problem of not being able to access the session object. I was trying to use CustomActionData but the sample code I grafted was setting it incorrectly. Your example is the correct way. Many Thanks! – Francis Jun 27 '17 at 07:38
  • I had hoped to use the CustomAction to set some global variable in my custom action DLL -- like a configuration value -- one that could be referenced by other CustomActions. But the configuration value does not persist across calls into the DLL; as though every call into the the DLL is bracketted with DLL_PROCESS_ATTACH/DLL_PROCESS_DETACH, so the config value is lost. – NoBrassRing Mar 26 '19 at 21:56
10

For us C++ schlubs, you retrieve the Property as follows:

MsiGetProperty(hInstall, "CustomActionData", buf, &buflen);

Then you parse 'buf'. Thank you to Bondbhai.

Pierre
  • 3,470
  • 1
  • 32
  • 33
5

If the value passed to the custom action is not a key/pair set...

i.e.

<SetProperty Id="CustomAction1" Before="CustomAction1" Value="data" Sequence="execute"/>
<CustomAction Id="CustomAction1" BinaryKey="BinaryId" DllEntry="MethodName" Execute="deferred"/>

...then the entire blob can be retrieved using:

string data = session["CustomActionData"];
ogggre
  • 1,895
  • 18
  • 18
Dave Andersen
  • 5,120
  • 2
  • 26
  • 28