Wixsharp: Suggested sample: change machine.config

Created on 19 Sep 2017  路  8Comments  路  Source: oleg-shilo/wixsharp

In one of our original wix setup we have (simplified) the following. I've found it hard to get it converted to wixsharp and instead went for alternative solution. Maybe a good idea as a sample?

<Property Id="NET40_32_BITS_MACHINE_CONFIG">
  <DirectorySearch Id="NET40_32_BITS_MACHINE_CONFIG_DIR" Path="[WindowsFolder]Microsoft.NET\Framework\v4.0.30319\CONFIG\" AssignToProperty="yes">
    <FileSearch Id="NET40_32_BITS_MACHINE_CONFIG_FILE" Name="machine.config" />
  </DirectorySearch>
</Property>

  <Component Id="RegisterAdoNet40_32bits" Guid="DF03D046-8DB0-4FA6-A374-36E74817B60A">
    <Condition><![CDATA[NET40_32_BITS_MACHINE_CONFIG <> ""]]></Condition>
    <!-- install -->
    <util:XmlConfig
          Id="Machine_Config_NET40_32_DPF_RemoveExisting"
          File="[NET40_32_BITS_MACHINE_CONFIG]\machine.config"
          Action="delete"
          On="install"
          ElementPath="//configuration/system.data/DbProviderFactories"
          Node="element"
          VerifyPath="//configuration/system.data/DbProviderFactories/add[\[]@invariant='Invantive'[\]]"
          Sequence="1" />

    <util:XmlConfig
          Id="Machine_Config_NET40_32_DPF_Root"
          File="[NET40_32_BITS_MACHINE_CONFIG]\machine.config"
          Action="create"
          On="install"
          ElementPath="//configuration/system.data/DbProviderFactories"
          Name="add"
          Node="element"
          Sequence="2" />

    <util:XmlConfig
           Id="Machine_Config_NET40_32_DPF_name"
           File="[NET40_32_BITS_MACHINE_CONFIG]\machine.config"
           ElementPath="Machine_Config_NET40_32_DPF_Root"
           Name="name"
           Value="Invantive Data Provider"
           Sequence="3" />

    <util:XmlConfig
           Id="Machine_Config_NET40_32_DPF_invariant"
           File="[NET40_32_BITS_MACHINE_CONFIG]\machine.config"
           ElementPath="Machine_Config_NET40_32_DPF_Root"
           Name="invariant"
           Value="Invantive"
           Sequence="3" />

    <util:XmlConfig
           Id="Machine_Config_NET40_32_DPF_description"
           File="[NET40_32_BITS_MACHINE_CONFIG]\machine.config"
           ElementPath="Machine_Config_NET40_32_DPF_Root"
           Name="description"
           Value=".Net Framework Data Provider for Invantive"
           Sequence="3" />

    <util:XmlConfig
           Id="Machine_Config_NET40_32_DPF_type"
           File="[NET40_32_BITS_MACHINE_CONFIG]\machine.config"
           ElementPath="Machine_Config_NET40_32_DPF_Root"
           Name="type"
           Value="Invantive.Data.InvantiveClientFactory, Invantive.AdoNet, Version=!(bind.fileVersion.Invantive.AdoNet.dll), Culture=neutral, PublicKeyToken=15f3938228f8c5ae"
           Sequence="3" />

    <!-- uninstall -->
    <util:XmlConfig
           Id="Machine_Config_NET40_32_DPF_Uninstall"
           File="[NET40_32_BITS_MACHINE_CONFIG]\machine.config"
           Action="delete"
           On="uninstall"
           ElementPath="//configuration/system.data/DbProviderFactories"
           Node="element"
           VerifyPath="//configuration/system.data/DbProviderFactories/add[\[]@invariant='Invantive'[\]]"
           Sequence="1" />
  </Component>
question

All 8 comments

As for me I do prefer not declarative but procedural coding for tasks like this. And it is actually easy to port to C#.

May be it wasn't obvious where to look for it but there is a sample that shows a few techniques for adjusting the config file after the installation: "WinService_InstallUtil"

@silentnightwalk also shows his approach in https://github.com/oleg-shilo/wixsharp/issues/167#issuecomment-330633577 discussion.

@oleg-shilo, either I'm blind or the aforementioned sample (WinService_InstallUtil) doesn't really point to any technique(s) for adjusting the config file after installation. I wasn't able to find any information about modification config files. The only thing setup.cs hints at is the static Tasks class.

But although it contains a SetConfigAttribute() method, it's made for setting an XML node's attributes, not child nodes. This is a problem in my case, because my service is developed as a console application, hence my App.config file's <userSettings> node looks like this:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  (...)
  <userSettings>
    <MyApp.Properties.Settings>
      <setting name="InputPath" serializeAs="String">
        <value>C:\Input</value>
      </setting>
      (...)
    </MyApp.Properties.Settings>
  </userSettings>
</configuration>

So, I need to write the value of session.Property("INPUTPATH") inside the <value> node, not as an attribute of the <setting> node.
Maybe you can implement this in the (hopefully near 馃槈) future?

Next, I'll try the approach of @silentnightwalk in #167 and report my success over there.

My apologies I gave you a wrong reference. It is in fact "DeferredActions" sample.

Config files can only be updated from the deferred actions that is why the technique is demonstrated in this sample.

I even added a sample method that illustrates exactly your case:

```C#
static public void UpdateAsXml2(string configFile, bool installing = true)
{
var config = XDocument.Load(configFile);

if (installing)
    config.Select("configuration")
          .AddElement("userSettings")
          .AddElement("MyApp.Properties.Settings")
          .AddElement("setting", "name=InputPath;serializeAs=String")
          .AddElement("value", null, @"C:\Input");
else
    config.Select("configuration/userSettings/MyApp.Properties.Settings")
         ?.Parent.Remove();

config.Save(configFile);

}
```

@oleg-shilo thanks for pointing it out. Google can now find this issue easily for patching a machine.config and similar using WIXSharp.
We went for procedural road for this one similar to shown above since migrating it from the declarative code was too hard for our knowledge and the procedural approach works fine and allows for better debugging and logging.

@oleg-shilo, your snippet is just great. Also, thanks a lot for updating the sample just for me.

I used it as a starting point, but splitted un/install into different CustomActions (which are added, together with different Conditions, on Project's creation within the initial Setup.cs.
Therefore, I opted for XPathSelectElements() instead of your Select() as you can see in my latest comment on https://github.com/oleg-shilo/wixsharp/issues/167#issuecomment-331179518.

Nonetheless: thank you so much for providing me with so much tutorial input. Awesome help, awesome project, awesome Oleg! 馃槈

You are very welcome :)

Just updating my suggested code with a better approach that works if the required section already exist in the config file:
```C#
static public void UpdateAsXml2(string configFile, bool installing = true)
{
var config = XDocument.Load(configFile);

if (installing)
    doc.root.SelectOrCreate("userSettings/MyApp.Properties.Settings/setting")
       .SetAttributes("name=InputPath;serializeAs=String")
       .SetElementValue("value", @"C:\Input")
else
    config.Select("configuration/userSettings/MyApp.Properties.Settings")
         ?.Parent.Remove();

config.Save(configFile);

}
```

Awesome update - I'm going to apply this to my code! Once again: thanks a lot, Oleg! 馃憤

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mgkeeley picture mgkeeley  路  5Comments

kain64 picture kain64  路  4Comments

yfnfif picture yfnfif  路  3Comments

CADbloke picture CADbloke  路  4Comments

mattias-symphony picture mattias-symphony  路  3Comments