0

I created a command-line application and moved a lot of its config into a standard Settings file. All settings are declared as Scope = Application, because there is nothing user-specific about the logic in the application. I access the values throughout the code with

Properties.Settings.Default.<whatever>  

This works well as it runs automatically on a schedule. Updating values in the config file are reflected in the output.
Some time later, I created a basic GUI (in the same namespace) to launch the command-line application directly (through a separate constructor). I haven't done a huge amount of .Net programming, but I'm basically using my CLI application like a DLL (I don't know if there's a proper term for this; in my output folder, all I need is GUI.exe, CLI.exe and CLI.exe.config and it works). However, I've noticed that when launched this way, the CLI.exe.config file is not loaded; the CLI application uses only its compiled-in defaults. I was hoping the config file method would work in this instance.
I've tried the following methods to force loading the config file but so far have drawn a blank:

1:

ConfigurationManager.RefreshSection("appSettings")  

2:

ExeConfigurationFileMap configFile = new ExeConfigurationFileMap();
configFile.ExeConfigFilename = Path.Combine(Environment.CurrentDirectory, System.Reflection.Assembly.GetExecutingAssembly().GetName().Name + ".exe.config");
ConfigurationManager.OpenMappedExeConfiguration(configFile, ConfigurationUserLevel.None).Save(ConfigurationSaveMod.Modified);  
ConfigurationManager.RefreshSection("appSettings");  

3:

Properties.Settings.Default.Reload();  

None of these produce errors, but the Properties.Settings.Default.Value I have modified in the config file is not updated. Is there a way to accomplish what I need here?

Edit: here is a sample of my CLI.exe.config file if it helps illustrate what I'm trying to accomplish here:

<?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="CLI.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false"/>
    </sectionGroup>
  </configSections>
  <connectionStrings/>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>
  <applicationSettings>
    <CLI.Properties.Settings>
      <setting name="URLBase" serializeAs="String">
        <value>https://cloud.mycompany.com/</value>
      </setting>
      <setting name="URLPage" serializeAs="String">
        <value>/inventory.aspx#view/Invoice/</value> <!-- This is the value I'm trying to change -->
      </setting>
...

I should also mention that I've also tried 'applicationSettings' in place of 'appSettings' in the code above.

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Gargravarr
  • 635
  • 2
  • 10
  • 18
  • 1
    `ConfigurationManager.RefreshSection("appSettings")` works for me. Are you sure that app.config really updated? – Alexander Oct 20 '15 at 11:14
  • User settings in `Properties.Settings` shouldn't have anything to do with the `.config` file inside your output folder, they are stored in user's local app data folder. – vgru Oct 20 '15 at 12:52
  • GUI.exe will read GUI.exe.config not cli.exe.config - it's that simple. – Mike Miller Oct 20 '15 at 13:06
  • @MikeMiller I figured that was the design of the .Net framework, and an understandable one, since I'm not executing CLI.exe directly. What I need is for the same behaviour to happen though – Gargravarr Oct 20 '15 at 16:16
  • @Groo at compile-time, the .config file contains exactly the settings that were entered as defaults into the settings file. When i change these settings in the file and execute the CLI.exe directly, my changes are reflected. I simply want this behaviour to occur when I execute GUI.exe and call the same methods within CLI. – Gargravarr Oct 20 '15 at 16:20

2 Answers2

0

Make Sure the access modifier has been set to public. Launching with 'GetExecutingAssembly' will not load the CLI config if it has been triggered by the GUI.EXE since this IS the executing assembly. take a look at this:

Accessing app config across exe's

Community
  • 1
  • 1
bilpor
  • 3,467
  • 6
  • 32
  • 77
0

Actual config file which will have your runtime settings will depend on your application's entry point.

If you start your CLI.exe directly, it will use a user settings file inside a subfolder inside user's local app data folder, similar to:

AppData\Local\CLI\CLI.exe\1.2.3.4\user.config

However, if you use a different entry point (CLI.exe), which references GUI.exe, all calls to Properties.Settings will use a completely different user.config instance, located in (something like):

AppData\Local\GUI\GUI.exe\1.2.3.4\user.config

Application-wide settings file (.exe.config) which is placed in your output folder, next to the executable, only provides default settings for these user settings, or read-only application settings. Allowing the application to write to this file would mean it would have write access to Program Files subfolders, which is not possible without elevated permissions.

The simplest solution to get the same user settings when you change your entry point is to copy the user.config file from the CLI app data folder to the appropriate folder for the new entry point.

(update for app setting)

It works the same for application settings (i.e. .config files in your output folder). If you originally had:

<!-- CLI.exe.config -->
<configuration>

    <!-- setting section definitions -->
    <configSections>
        <sectionGroup name="userSettings" ... >
            <section name="CLI.CliSettings" ... />
        </sectionGroup>
        <sectionGroup name="applicationSettings" ... >
            <section name="CLI.CliSettings" ... />
        </sectionGroup>
    </configSections>

    <!-- defaults for user settings (runtime changes are stored to appdata) -->
    <userSettings>
        <CLI.CliSettings>
            <setting name="SomeUserCliSetting" serializeAs="String">
                <value>Some CLI user setting</value>
            </setting>
        </CLI.CliSettings>
    </userSettings>

    <!-- application settings (readonly) -->
    <applicationSettings>
        <CLI.CliSettings>
            <setting name="SomeAppCliSetting" serializeAs="String">
                <value>Some CLI app setting</value>
            </setting>
        </CLI.CliSettings>
    </applicationSettings>

</configuration>

And your GUI.exe.config was:

<!-- GUI.exe.config -->
<configuration>

    <configSections>
        <sectionGroup name="userSettings" ... >
            <section name="GUI.GuiSettings" ... />
        </sectionGroup>
        <sectionGroup name="applicationSettings" ... >
            <section name="GUI.GuiSettings" ... />
        </sectionGroup>
    </configSections>

    <userSettings>
        <GUI.GuiSettings>
            <setting name="SomeGuiUserSetting" serializeAs="String">
                <value>Some GUI user setting</value>
            </setting>
        </GUI.GuiSettings>
    </userSettings>

    <applicationSettings>
        <GUI.GuiSettings>
            <setting name="SomeGuiAppSetting" serializeAs="String">
                <value>Some GUI app setting</value>
            </setting>
        </GUI.GuiSettings>
    </applicationSettings>

</configuration>

Then your resulting .config file should contain all sections:

<!-- GUI.exe.config -- merged configuration sections -->
<configuration>

    <configSections>
        <sectionGroup name="userSettings" ... >
            <section name="GUI.GuiSettings" ... />
            <section name="CLI.CliSettings" ... />
        </sectionGroup>
        <sectionGroup name="applicationSettings" ... >
            <section name="GUI.GuiSettings" ... />
            <section name="CLI.CliSettings" ... />
        </sectionGroup>
    </configSections>

    <userSettings>
        <GUI.GuiSettings>
            <setting name="SomeGuiUserSetting" serializeAs="String">
                <value>Some GUI user setting</value>
            </setting>
        </GUI.GuiSettings>
        <CLI.CliSettings>
            <setting name="SomeUserCliSetting" serializeAs="String">
                <value>Some CLI user setting</value>
            </setting>
        </CLI.CliSettings>
    </userSettings>

    <applicationSettings>
        <GUI.GuiSettings>
            <setting name="SomeGuiAppSetting" serializeAs="String">
                <value>Some gui app setting</value>
            </setting>
        </GUI.GuiSettings>
        <CLI.CliSettings>
            <setting name="SomeAppCliSetting" serializeAs="String">
                <value>Some CLI app setting</value>
            </setting>
        </CLI.CliSettings>
    </applicationSettings>

</configuration>

(it's possible I mixed up some values while writing this, but you get the idea).

There is also a tool I found by googling: XmlConfigMerge on CodeProject - I haven't tried it but presumably it does the same thing automatically, so you might want to check it out and include it into your build scripts.

vgru
  • 49,838
  • 16
  • 120
  • 201
  • Btw, I don't use .NET application settings for many reasons (like the one you found out, or versioning/upgrade issues I've had when publishing new app versions). Our company's Settings class, for example, loads and stores settings to a app data folder/file based on dll assembly name, so that each assembly has its own config file. **But**, having said that, there are cases where you want each entry point to have a different set of settings for the same assembly, and with .NET settings you get a new `user.config` file for each entry point (containing settings for all referenced assemblies). – vgru Oct 20 '15 at 13:23
  • I checked the AppData folders and there are no Local settings for either application; as I noted, all settings are Application scope, not User scope, so there would be no need to create them. You say that the Settings file next to the executable should provide defaults - this is exactly the use I want because these values will not change at runtime, but should be changeable beforehand. However, this is not happening. – Gargravarr Oct 20 '15 at 16:13
  • @Gargravarr: It works the same with app settings, you just need to merge settings into the `.config` file for the entry point. The idea behind .NET app settings is to allow each entry point to have a separate set of config settings. Check my edit. – vgru Oct 21 '15 at 10:46
  • your edit looks good, but I don't have a config file for the GUI because it does not require any; all parameters are passed on the command line (things like the connection string, which is different for every environment it runs on, so this is set in a shortcut). Do you think I need to create a config file for the GUI? – Gargravarr Oct 21 '15 at 16:23
  • @Gargravarr: if you don't have a config for the GUI, then all you need to do (probably, I am not near a PC right now) should be to copy the CLI.exe.config into GUI.exe.config in your output folder, or simply copy the `app.config` from CLI to GUI. It's only important to make sure your sections are qualified with the correct (CLI) namespace (e.g. `CLI.CliSettings` in the example above). But, yes, you can also create a `app.config` for the GUI project and add these sections there directly. – vgru Oct 21 '15 at 17:03
  • I tried copying the settings file and changing the namespace, but it still isn't loading :( – Gargravarr Oct 27 '15 at 10:11
  • @Gargravarr: that's weird. I just tried this and is seems to be working (unless I am mistaken). [Here is the zip for a solution with two projects, CLI and GUI](http://www.filefactory.com/file/6j8yenncj4yp/GUI_and_CLI.zip). GUI is set as the startup project and calls a CLI's method which reads CLI's internal settings, but since GUI is the entry point, settings are read from GUI's app.config. – vgru Oct 28 '15 at 14:08