Updating .config file from an Custom Inst

2020-05-24 06:35发布

问题:

I've tried updating my application's .config file during installer (via a .NET Installer Class Action). But, I can't seem to get ConfigurationManager to list any properties or be able to set anything.

I learned of this approach from several stackoverflow posts that pointed me to this guide: http://raquila.com/software/configure-app-config-application-settings-during-msi-install/

I've investigated the differences between my project and his and noticed that my configuration files are formatted differently. I believe this has to do with the fact that i'm using "Settings" files.

Config files formatted in the guide look like:

<?xml version="1.0" encoding="utf-8" ?>     
<configuration>     
  <appSettings>      
    <add key="Param1" value="" />      
    <add key="Param2" value="" />      
    <add key="Param3" value="" />      
  </appSettings>      
</configuration> 

Where mine looks like:

   <?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
            <section name="MyAppName.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
            <section name="MyAppName.Settings1" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
        </sectionGroup>
        <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
            <section name="MyAppName.Settings1" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
        </sectionGroup>
    </configSections>
    <userSettings>
        <MyAppName.Properties.Settings>
            <setting name="TESTSETTING" serializeAs="String">
                <value>asdfasdfasdf</value>
            </setting>
        </MyAppName.Properties.Settings>
        <MyAppName.Settings1>
            <setting name="VerboseErrorMode" serializeAs="String">
                <value>False</value>
            </setting>
    <applicationSettings>
        <MyAppName.Settings1>
            <setting name="RunOnStartup" serializeAs="String">
                <value>True</value>
            </setting>
        </MyAppName.Settings1>
    </applicationSettings>
</configuration>

To shed some light on what was going on... I tried printing out the list of settings like so:

            Configuration config = ConfigurationManager.OpenExeConfiguration(exePath);               

            // Try getting the Settings1 Section
            AppSettingsSection appSettings = (AppSettingsSection)config.GetSection("Settings1");  // Also tried myNamespace.Settings1
            if (appSettings != null)
            {
                valList = "Settings1: ";
                foreach (string key in appSettings.Settings.AllKeys)
                {
                    string value = appSettings.Settings[key].Value;
                    valList += ("Key: '" + key + "' = '" + value + "'\n");
                }
            }
            else
            {
                valList = "appSettings was null";
            }
            MessageBox.Show(valList);

        MessageBox.Show(valList);

I have tried several permutations of this... and in all cases output is "appSettings was null".

I also tried initializing the configuration manager in several different ways...

            Configuration config = ConfigurationManager.OpenExeConfiguration(exePath);

            MessageBox.Show("Section Count: " + config.Sections.Count);
            MessageBox.Show("Has File: " + config.HasFile);
            MessageBox.Show("Namespace Declared: " + config.NamespaceDeclared);

            config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
            MessageBox.Show("Section Count: " + config.Sections.Count);
            MessageBox.Show("Has File: " + config.HasFile);
            MessageBox.Show("Namespace Declared: " + config.NamespaceDeclared);

For each of them the section count returned was 20. (I have no idea where the 20 comes from... I would have expected it to be 3).
HasFile was true for the first case and false for the second.
Namespace Declared was false in both cases.

Thanks!

EDIT (6-18-09): Still looking into this question. Anyone else have any ideas? Thanks.

Search Keywords: "Object Reference Not Set to not set to an instance" <-- this occurs when trying to write to a property.

回答1:

I came across the same problem, after deep investigation, I found out the easiest way to update any part of config files (e.g. app.config), that's by using XPath. We have an application which connects to web service, during the installation, the user enters the URL of the web service and this should be saved in the following app.config file:

        <?xml version="1.0"?>
    <configuration>
      <configSections>
        <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
          <section name="ApplicationServer.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
        </sectionGroup>
      </configSections>

      <applicationSettings>
        <ApplicationServer.Properties.Settings>
          <setting name="ApplicationServer_ApplicationServerUrl" serializeAs="String">
            <value>whatever comes from setup should go here</value>
          </setting>
        </ApplicationServer.Properties.Settings>
      </applicationSettings>
    </configuration>

Here is the code to do this in the installer class:

public override void Install(System.Collections.IDictionary stateSaver)
    {
        base.Install(stateSaver);

        string targetDirectory = Context.Parameters["targetdir"];
        string param1 = Context.Parameters["param1"];

        string path = System.IO.Path.Combine(targetDirectory, "app.config");

        System.Xml.XmlDocument xDoc = new System.Xml.XmlDocument();

        xDoc.Load(path);

        System.Xml.XmlNode node = xDoc.SelectSingleNode("/configuration/applicationSettings/Intellisense.ApplicationServer.Properties.Settings/setting[@name='ApplicationServer_ApplicationServerUrl']/value");
        node.InnerText = (param1.EndsWith("/") ? param1 : param1 + "/");

        xDoc.Save(path); // saves the web.config file  
    }

Basically, since the config file is a XML based document, I am using XPath expression to locate specific node and change its value.



回答2:

One thing to try is moving it from install to Commit to ensure that the file has been written first before trying to access it. Alternatively you could use the custom action to write your own file and just alter your config file to point at the alternative xml file.

I worked on a Sharepoint product install where I dug into all of this and I will admit it is very tedious to get working correctly. I was creating config and batch files on the fly based on install parameters.



回答3:

You can access these settings using the System.Configuration namespace but, its not as simple as I would like and in retrospect using System.Xml.Linq is far simpler. Anyway, here is how I got it to work.

The important concept is, the applicationSettings section is not AppSettings, its a seperate section supported by the ClientSettingsSection type.

//Open the application level config file
ExeConfigurationFileMap exeMap = new ExeConfigurationFileMap();
exeMap.ExeConfigFilename = String.Format("{0}.config", 
    Context.Parameters["assemblypath"]);
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(exeMap,
    ConfigurationUserLevel.None);

//Get the settings section
ClientSettingsSection settingsSection =
    config.GetSectionGroup("applicationSettings").Sections
         .OfType<ClientSettingsSection>().Single();

//Update "TheSetting"
//I couldn't get the changes to persist unless
//I removed then readded the element.

SettingElement oldElement = settingsSection.Get("TheSetting");
settingsSection.Settings.Remove(oldElement);

SettingElement newElement = new SettingElement("TheSetting", 
    SettingSerializeAs.String);
newElement.Value = new SettingValueElement();
newElement.Value.ValueXml = oldElement.Value.ValueXml.CloneNode(true);
newElement.Value.ValueXml.InnerText = "Some New Value";
settingsSection.Add(newElement);

//Save the changes
config.Save(ConfigurationSaveMode.Full);

So, as you can see, simple. :-S



回答4:

Instead of accessing your settings configuration via ConfigurationManager, you should be able to access it via Settings.Default. Settings are more of a Visual Studio feature than a .NET feature...a convenience that makes it easy to visually design your applications configuration rather than manually writing it in appSettings or creating custom configuration sections. However, the configuration schema that is rendered when you use Settings is non-standard, and can be difficult to access manually.

Visual Studio should have generated a Settings class for you when you built your application, and you should be able to access that class via Properties.Settings.Default. It should have a property for each setting, in your case, the following:

Properties.Settings.Default.TESTSETTING
Properties.Settings.Default.VerboseErrorMode
Properties.Settings.Default.RunOnStartup

You should be able to both read and write these settings. A critical thing to note...anything flagged as a "user" setting will not be written back to the {yourapplication}.exe.config file...it will be written to a User.config file in the users isolated profile storage area. This is under C:\Documents and Settings{username} on XP, and C:\Users{username} on Vista, in the AppData folder. Depending on the OS and users profile, the subfolder under the AppData may change, but its completely unique and keyed to a particular version of the application. Installation of a newer version will result in a completely new set of configuration settings stored under the same keyed folder, but a different version subfolder.

I hope this helps. :)



回答5:

This is what you need: http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/f89a00eb-9400-48ce-af20-cef78002c14e