Is there any supported API in .NET 2.0 for writing to the userSettings section of the main exe's .config file?
The scenario is:
Winforms 2.0 application.
I have a setting (a database connection string, if you need to know) that has user level scope. This means that each user has a user.config file created by .net when the user saves the value of the setting.
For new users that run the application for the first time, the main exe's .config file of the application contains a default value in the user settings section. This section is created by visual studio when the setting is created in the Settings tab of the project properties.
Now, I want to allow any Administrator user in the computer to be able to change the default value for new users. Only Administrators will have this option, because regular users don't have permission to write to the main exe's .config file anyway.
I have found how to write user settings to the user's .config file, and how to write to the appSettings section of the main .config file. But my googling has failed when trying to find out how to write to the userSettings section of the main .config
Is my only chance failing back to System.Xml and do it manually loading the .config in an XmlDocument?
After some research I came up with this solution. It is a bit low level, but still goes through the .NET configuration API without having to manually parse the .config file.
static void SaveUserSettingDefault(string clientSectionName, string settingName, object settingValue)
{
System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
// find section group
ConfigurationSectionGroup group = config.SectionGroups[#"userSettings"];
if (group == null) return;
// find client section
ClientSettingsSection clientSection = group.Sections[clientSectionName] as ClientSettingsSection;
if (clientSection == null) return;
// find setting element
SettingElement settingElement = null;
foreach (SettingElement s in clientSection.Settings)
{
if (s.Name == settingName)
{
settingElement = s;
break;
}
}
if (settingElement == null) return;
// remove the current value
clientSection.Settings.Remove(settingElement);
// change the value
settingElement.Value.ValueXml.InnerText = settingValue.ToString();
// add the setting
clientSection.Settings.Add(settingElement);
// save changes
config.Save(ConfigurationSaveMode.Full);
}
Given a .config with the following content:
<?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="MyAssembly.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
</configSections>
<userSettings>
<MyAssembly.Properties.Settings>
<setting name="SqlConnectionString" serializeAs="String">
<value>Server=(local);Database=myDatabase;Integrated Security=true;</value>
</setting>
</MyAssembly.Properties.Settings>
</userSettings>
</configuration>
You would use it like this:
if (RunningAsAdmin) // save value in main exe's config file
{
SaveUserSettingDefault(#"MyAssembly.Properties.Settings", #"SQLConnectionString", theNewConnectionString);
}
else // save setting in user's config file
{
Settings.Default. SQLConnectionString = theNewConnectionString;
Settings.Default.Save();
}
I think yes - manually writing to the .config file is the only way.
Or you can let the administrator edit the .config-file himself.
Related
Here is my appconfig:
<connectionStrings>
<add name ="cn" connectionString="User ID=YOUR_USER_HERE; Password=YOUR_PASS_HERE;Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=.)(PORT=1521)))(CONNECT_DATA=(SERVICE_NAME=.)));"/>
</connectionStrings>
I have tried this function:
public void updateConfigFile(string con)
{
//updating config file
XmlDocument XmlDoc = new XmlDocument();
//Loading the Config file
XmlDoc.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
foreach (XmlElement xElement in XmlDoc.DocumentElement)
{
if (xElement.Name == "connectionStrings")
{
{
xElement.FirstChild.Attributes[1].Value = con;
}
}
}
//writing the connection string in config file
XmlDoc.Save("App.config");
}
That did not work. It ran without any error. But it did not save connectionString into appconfig file. I also tried this idea:
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var connectionStringsSection = (ConnectionStringsSection)config.GetSection("connectionStrings");
connectionStringsSection.ConnectionStrings["Blah"].ConnectionString = "Data Source=blah;Initial Catalog=blah;UID=blah;password=blah";
config.Save();
ConfigurationManager.RefreshSection("connectionStrings");
I get an error that says UnauthorizedAccess. ( I believe it is because of my school network)
These two options have not worked so far. I also tried to set ConfigurationManager.ConnectionStrings["cn"] to my custom connectionString. Since it is readonly, that failed too. Is there any other idea that I can replace connectionString at runtime?
Thanks in advance.
Changing the app configuration file must be done before your application starts, as certain keys are read by the runtime when your application is being bootstrapped only, and others are turned into readonly.
You can dynamically change the configuration entries of the AppSettings section only by referencing the System.Configuration assembly. All the other keys are by design read only.
If you don't want to alter the configuration before starting the application, you need to think about all the involved entities: who is going to read your connection string? If it is you, you can simply store it somewhere else or even in the AppSettings key. Instead, if external components require to read it from your configuration file, you have no chance that changing the architecture of your application in order to have a wrapper that does the changes before running your application.
The AppSettings key ca be changed in this way, after refencing the System.Configuration assembly (this is vital).
App.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="key1" value="configuration"/>
</appSettings>
</configuration>
Program.cs
using System;
using System.Configuration;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
System.Configuration.ConfigurationManager.AppSettings["key1"] = "changed";
var value = System.Configuration.ConfigurationManager.AppSettings["key1"];
Console.WriteLine($"This is the new key: {value}.");
}
}
}
string appConfPath = Directory.GetParent(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)).FullName;
string fullPath = appConfPath + "\\Local\\RandFolder\\ThisOne\\application.settings.xml";
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
config.AppSettings.File = fullPath;
config.AppSettings.Settings.Add("Password", "djydyjdjtdtyjddj");
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("appSettings");
var temp = config.AppSettings;
I am currently trying to set the "file" attribute of my appSettings configuration and reference XML's included settings at runtime. I am unable to do this before compile because the XML settings file will change based on the local machines settings.
However, after making the above changes, the temp variable only contains the "Password" item and is unable to retrieve the other settings located in the included file path. I know the file attribute is being set but for some reason the referenced setting are still hidden. The application.settings.xml file looks like this...
<?xml version="1.0" encoding="utf-8"?>
<appSettings>
<add key="ServerLocation" />
<add key="PerforceURI" value="yuhsgbyluvgblsg" />
</appSettings>
Any help is greatly appreciated!
I won't try to critique what you are doing, but provide you with a simple explanation of what you are seeing.
ConfigurationManager.RefreshSection refreshes the section in the Configuration instance used by the static ConfigurationManager. It does not effect the Configuration instance you created by calling OpenExeConfiguration; for that to occur you would need to call OpenExeConfiguration again.
I have a WinForm derived application (note NOT an ASP.NET web application) from where I need to modify a custom section of an arbitrary web.config file. As an example, if my web.config is something like this:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- General web.config stuff follows -->
<system.web>
<httpRuntime executionTimeout="110" maxRequestLength="1024" requestValidationMode="2.0" />
</system.web>
<MyConfigSection>
<GeneralParameters>
<param key="Var1" value="value1" />
</GeneralParameters>
</MyConfigSection>
</configuration>
I can easily modify some default parameter, say, for maxRequestLength I'd do this and it would work:
//Path to the web.config file
string strWebConfigFile = #"C:\My files\web.config";
//Convert absolute path to virtual
var configFile = new FileInfo(strWebConfigFile);
var vdm = new VirtualDirectoryMapping(configFile.DirectoryName, true, configFile.Name);
var wcfm = new WebConfigurationFileMap();
wcfm.VirtualDirectories.Add("/", vdm);
//Open web.config file
System.Configuration.Configuration config =
System.Web.Configuration.WebConfigurationManager.OpenMappedWebConfiguration(wcfm, "/");
if (config != null)
{
System.Configuration.ConfigurationSection system_web =
config.GetSection("system.web/httpRuntime");
PropertyInformation pi = system_web.ElementInformation.Properties["maxRequestLength"];
pi.Value = 1234; //Set new value
//Save
config.Save(ConfigurationSaveMode.Modified);
}
The issue is when I try to modify my custom section. Say, if I wanted to rewrite Var1 parameter's value with value2, the following:
System.Configuration.ConfigurationSection genParams = config.GetSection("MyConfigSection/GeneralParameters");
returns null and if I call it with just MyConfigSection, it gives me this exception:
An error occurred creating the configuration section handler for
MyConfigSection: Could not load type 'MyWebApp.Configuration' from
assembly 'System.Web, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=N'.
What shall I do here to add that "configuration section handler"?
You have to open it up with XmlDocument, and work with it, sorry. That is how we cracked this nut - sorry I'm not at liberty to give you the code.
Is it possible to retrieve a custom configuration section from a config file other than the app.config or web.config.
I tried using the System.Configuration.ConfigurationManager's OpenExeConfiguration and GetSection method calls together but without luck. My intention is to define custom configuration sections for interchangeable process adapters and contain the custom config section in a separate config file other than app.config and web.config. I see plenty of examples for appsettings and connectionstrings.
static private DigiKeyReadTaskConfigSection digiKeyReadTaskConfigSection;
static DigiKeyReadTaskConfigSection DigiKeyReadTaskConfigSection {
get {
if (digiKeyReadTaskConfigSection == null) {
digiKeyReadTaskConfigSection = (DigiKeyReadTaskConfigSection)ConfigurationManager.OpenExeConfiguration("ReadTask.config").GetSection("DigiKeyReadTaskConfigSection");
}
return digiKeyReadTaskConfigSection;
}
}
The digiKeyReadTaskConfigSection = (DigiKeyReadTaskConfigSection)ConfigurationManager.OpenExeConfiguration call seems to be working however the (DigiKeyReadTaskConfigSection)ConfigurationManager.OpenExeConfiguration("ReadTask.config").GetSection("DigiKeyReadTaskConfigSection") returns null.
The ReadTask.config file lives in the bin file of the App:
<configuration> <configSections>
<section name="DigiKeyReadTaskConfigSection" type="DataReadInc.WebSiteRead.TaskConfigSection.DigiKeyReadTaskConfigSection, DataReadInc.WebSiteRead" />
<section name="ArrowReadTaskConfigSection" type="DataReadInc.WebSiteRead.TaskConfigSection.ArrowReadTaskConfigSection, DataReadInc.WebSiteRead" /> </configSections> <DigiKeyReadTaskConfigSection DigiKeySiteURL="http://search.digikey.com/scripts/DkSearch/dksus.dll?WT.z_header=search_go&lang=en&site=us&keywords="
SiteLogInURL="https://ordering.digikey.com/RegisteredUser/Login.aspx,formName=" SiteLoginId="X" SiteLoginPassword="X" /> <ArrowReadTaskConfigSection ArrowAmericaSiteURL="http://components.arrow.com/part/search/"
SiteLoginURL="http://components.arrow.com/login/processlogin#" SiteLoginId="X" SiteLoginPassword="X" /> </configuration>
I have seen this type of setup with Spring.Net and a J2EE implementation so I am sure it is possible. I can just put my custom config sections in the App.config or web.config file, however it would be significantly cleaner for them to exist in their own config file.
Use ConfigurationManager.OpenMappedExeConfiguration(). OpenExeConfiguration relates to a certain exe.
I wanna read/write (and save) application's configuration file in program
The app.config is like this:
<configuration>
<configSections>
<section name="AdWordsApi" type="System.Configuration.DictionarySectionHandler" requirePermission="false"/>
</configSections>
<AdWordsApi>
<add key="LogPath" value=".\Logs\"/>
...
</AdWordsApi>
</configuration>
When I use ConfigurationManager.GetSection to read the app.config, it works:
var adwords_section = (System.Collections.Hashtable) System.Configuration.ConfigurationManager.GetSection("AdWordsApi");
Console.WriteLine((string)adwords_section["LogPath"]);
But when I use ConfigurationManager.OpenExeConfiguration:
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal);
ConfigurationSection section = config.GetSection("AdWordsApi");
Console.WriteLine(section["LogPath"]);
I always get this error:
'System.Configuration.ConfigurationElement.this[System.Configuration.ConfigurationProperty]'
is inaccessible due to its protection level
But as I know, GetSection cannot save configuration at program runtime, Like I said at beginning: I wanna save configuration at program runtime, So I have to use OpenExeConfiguration.
I have googled for long time, what I found is to use AppSettings, but what I use is custom section..
Anyone could explain why this "ConfigurationProperty is inaccessible" error occured? Thanks
Edit:
I have set copy local of System and System.Configuration to true
string key_value = refconfig.AppSettings.Settings["key_name"].Value;
You can use this article.
Edit:
you can use config:
<configSections>
<section name="AdWordsApi.appSettings" type="System.Configuration.AppSettingsSection" />
</configSections>
<AdWordsApi.appSettings>
<add key="LogPath" value=".\Logs\"/>
</AdWordsApi.appSettings>
this code:
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal);
var settings = config.GetSection("AdWordsApi.appSettings") as AppSettingsSection;
if (settings != null) Console.Write(settings.Settings["LogPath"].Value);
Console.ReadLine();
Also You can use this article.
I'm not sure if it will work for what you are trying to do, but have you tried using ConfigurationUserLevel.None instead?