How to add multiple elements to an XML configuration file using wix?

I am trying to edit an XML file using Wix. I am using WixUtilExtension bundled with Wix 3.7. An xml file is a settings file created in Visual Studio 2010 for a C # application. In this file, I use an element that is used to store several string values ​​in an array. This is the contents of the unmodified settings file:

<configuration> <applicationSettings> <AppName.Properties.Settings> <setting name="StringArray" serializeAs="Xml"> <value> <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> </ArrayOfString> </value> </setting> </AppName.Properties.Settings> </applicationSettings> </configuration> 

I want to add <string> elements to a <ArrayOfString> element in this file. One way to do this is to use the <XmlConfig> element from the wix / UtilExtension namespace. I added this element to the component that contains the configuration file as follows:

 <Component Id="ProductComponent" Guid="$(var.ConfigGuid)"> <File Source="SettingsFile.exe.config" KeyPath="yes" Id="FILE_config" /> <util:XmlConfig Name="string" Value="My value" File="[INSTALLFOLDER]SettingsFile.exe.config" Id="String1" On="install" Action="create" Node="element" ElementPath="/configuration/applicationSettings/AppName.Properties.Settings/setting[\[]@name='StringArray'[\]]/value/ArrayOfString" Sequence="100" /> </Component> 

This causes one <string> element to be added to the <ArrayOfString> element. To add another <string> element to the settings file, another XmlConfig element with a different identifier attribute and a higher value for the Sequence attribute is added to the <Component> element of the installation project as follows:

 <util:XmlConfig Name="string" Value="My second value" File="[INSTALLFOLDER]SettingsFile.exe.config" Id="String2" On="install" Action="create" Node="element" ElementPath="/configuration/applicationSettings/AppName.Properties.Settings/setting[\[]@name='StringArray'[\]]/value/ArrayOfString" Sequence="101" /> 

After msi is installed, the <ArrayOfString> element in the settings file looks like this:

 <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <string>My value</string><string>My second value</string></ArrayOfString> 

I found out that you can set the Value attribute of the <XmlConfig> attribute for a property value like this:

 <Property Id="STRING1VALUE" Value="My value" /> <util:XmlConfig Value="[STRING1VALUE]" ... /> 

It's good. I would like the user to be able to dynamically add multiple values ​​to the installation process in order to add a variable number of <string> elements to the settings file. My first approach was to use the <?foreach?> Statement as follows:

 <?define values="My value;My second value"?> <?foreach value in $(var.values)?> <util:XmlConfig Name="string" Value="$(var.value)" File="[INSTALLFOLDER]SettingsFile.exe.config" Id="String$(var.value)" On="install" Action="create" Node="element" ElementPath="/configuration/applicationSettings/AppName.Properties.Settings/setting[\[]@name='StringArray'[\]]/value/ArrayOfString" Sequence="101" /> <?endforeach?> 

There are several problems with this approach:

  • The foreach statement uses a preprocessor variable that cannot be set to a property value.
  • The value of the Sequence attribute remains unchanged.

I would like the user to save the values ​​for the string elements in the Property, which separate the values ​​into semicolons, and then parse them in the foreach statement as follows:

 <Property Id="VALUES" Value="My value;My second value" /> <?foreach value in [VALUES]?> <util:XmlConfig Name="string" Value="$(var.value)" File="[INSTALLFOLDER]SettingsFile.exe.config" Id="String$(var.value)" On="install" Action="create" Node="element" ElementPath="/configuration/applicationSettings/AppName.Properties.Settings/setting[\[]@name='StringArray'[\]]/value/ArrayOfString" Sequence="101" /> <?endforeach?> 

This causes the following error:

 The util:XmlConfig/@Id attribute value, 'String[VALUES]', is not a legal identifier. Identifiers may contain ASCII characters AZ, az, digits, underscores (_), or periods (.). Every identifier must begin with either a letter or an underscore. 

Is there a way to create a variable number of elements using an XmlFile or XmlConfig element? The only solution to this problem is CustomAction?

+6
source share
3 answers

Based on Rob's answer, here is my new approach to adding multiple elements to an XML configuration file using Wix. I did not want to write C ++ code, so I used DTF in my CustomAction.

I’ll show you how to turn a string containing multiple elements using a delimiter to multiple XML elements.

First, the installation file must have a property containing a delimited string.

 <Property Id="STRINGARRAY" Value="string1;string2;string3" /> 

This property can be populated by the user in the dialog box, of course.

Then you need to write CustomAction. To use DTF, a link to the Microsoft.Deployment.WindowsInstaller.dll file must be added to the C # CustomAction project. The Microsoft.Deployment.WindowsInstaller namespace must be enabled using the using directive in this project. My CustomAction looks like this:

 [CustomAction] public static ActionResult Insert(Session session) { string strings = session["STRINGARRAY"]; string[] stringArray = strings.Split(';'); Database db = session.Database; View view = db.OpenView("select * from `XmlConfig`"); string xpath = "/configuration/applicationSettings/AppName.Properties.Settings/setting[\\[]@name='StringArray'[\\]]/value/ArrayOfString"; for (int i = 0; i < stringArray.Length; i++) { string id = String.Format("String{0}", i); int sequence = 100 + i; string value = stringArray[i].Trim(); Record rec = new Record( id, "[INSTALLFOLDER]SettingsFile.exe.config", xpath, null, "string", value, 273, "ProductComponent", sequence); view.InsertTemporary(rec); } db.Close(); return ActionResult.Success; } 

Here, first, the StringArray property is read into a local variable, which is converted to a string array. The next line connects to the current database used by the installer. An XmlConfig table handle is created, which is a table into which XML elements are added. To insert the correct values ​​into this table, it is best to create an installer file that contains such a table, and then look at this table in an editor such as orca or InstEd.

In xpath, backslashes must be escaped with a double backslash. The id variable contains the name of the temporary record using a simple string, and the number works flawlessly. The sequence should be increased for each element. I could not find the documentation on the values ​​of the flags column, but I found that for the values ​​that were created, the values ​​for its elements were set to 273 and 289 for elements that were deleted.

Once the record is filled with the correct values, it is added to the XmlConfig table using the InsertTemporary method of the view object. This is done for each item found in the separation line.

The problem I am facing is that this CustomAction fails if the XmlConfig table does not exist. To counter this problem, I added the following code to the installation file, which adds the element to the XML file and immediately removes this element. I suppose there might be a cleaner solution, but that was the easiest for me.

 <util:XmlConfig Name="string" Value="Dummy" File="[INSTALLFOLDER]SettingsFile.exe.config" Id="DummyEntry" On="install" Action="create" Node="element" ElementPath="/configuration/applicationSettings/AppName.Properties.Settings/setting[\[]@name='StringArray'[\]]/value/ArrayOfString" Sequence="1" /> <util:XmlConfig On="install" Action="delete" Id="DeleteDummyEntry" Node="element" File="[INSTALLFOLDER]SettingsFile.exe.config" VerifyPath="/configuration/applicationSettings/AppName.Properties.Settings/setting[\[]@name='StringArray'[\]]/value/ArrayOfString/string" ElementPath="/configuration/applicationSettings/AppName.Properties.Settings/setting[\[]@name='StringArray'[\]]/value/ArrayOfString" Sequence="2" /> 

Finally, CustomAction should be added to the installation project. By adding a link to the CustomAction project in the installation project, the location of the binary file can be specified as follows:

 <Binary Id="XmlCustomActionDLL" SourceFile="$(var.XmlCustomAction.TargetDir)XmlCustomAction.CA.dll" /> 

The CustomAction user must be executed immediately, otherwise he will not be able to access the session variable:

 <CustomAction Id="CA_XmlCustomAction" BinaryKey="XmlCustomActionDLL" DllEntry="Insert" Execute="immediate" Return="check" /> <InstallExecuteSequence> <Custom Action="CA_XmlCustomAction" Before="RemoveRegistryValues" /> </InstallExecuteSequence> 

In order to determine the correct position for CustomAction in the installation sequence, I relied on this article by Bob Arnson.

+3
source

As a complement to BdN3504, answer ... instead of the whole

 <util:XmlConfig Name="string" Value="Dummy" File="[INSTALLFOLDER]SettingsFile.exe.config" Id="DummyEntry" On="install" Action="create" Node="element" ElementPath="/configuration/applicationSettings/AppName.Properties.Settings/setting[\[]@name='StringArray'[\]]/value/ArrayOfString" Sequence="1" /> <util:XmlConfig On="install" Action="delete" Id="DeleteDummyEntry" Node="element" File="[INSTALLFOLDER]SettingsFile.exe.config" VerifyPath="/configuration/applicationSettings/AppName.Properties.Settings/setting[\[]@name='StringArray'[\]]/value/ArrayOfString/string" ElementPath="/configuration/applicationSettings/AppName.Properties.Settings/setting[\[]@name='StringArray'[\]]/value/ArrayOfString" Sequence="2" /> 

thing. I would suggest using

  <EnsureTable Id='XmlConfig' /> 

This ensures that the XmlConfig table is included in the output MSI, even if it is empty. (I would just say this as a comment .. but I have no reputation, apparently)

+3
source

Yes, it is possible, but if you want it to be determined during installation, the preprocessor is not an option. The preprocessor is executed during the assembly process.

To get what you want, you need to write another custom action that takes an arbitrarily long set of user data and adds temporary rows to the XmlConfig table. The WcaAddTempRecord() function in src\ca\wcautil\wcawrap.cpp can do the job. src\ca\wixca\dll\RemoveFoldersEx.cpp is a pretty good example of using WcaAddTempRecord() to add rows to the RemoveFile table. You will also want to do this.

0
source

All Articles