ODBC connection string (DSN) - c#

I'm developing a plugin for Excel (for customizable reporting and data extraction from various sources).
Right now I'm struggling with ODBC connection (DSN in particular). Even though I have created a dedicated DSN and pointed the app to use it, I had to specify the same information in the connection string (pwd, user name, db name etc.) to make it work.
I don't want to expose this type of information in the xml file and wonder if it possible to force the app to use the info from DSN only (or, as alternative, to encode the conn. string in the customized xml file)?

You may want to encrypt connection string. Details here
static void ToggleConfigEncryption(string exeConfigName)
{
// Takes the executable file name without the
// .config extension.
try
{
// Open the configuration file and retrieve
// the connectionStrings section.
Configuration config = ConfigurationManager.
OpenExeConfiguration(exeConfigName);
ConnectionStringsSection section =
config.GetSection("connectionStrings")
as ConnectionStringsSection;
if (section.SectionInformation.IsProtected)
{
// Remove encryption.
section.SectionInformation.UnprotectSection();
}
else
{
// Encrypt the section.
section.SectionInformation.ProtectSection(
"DataProtectionConfigurationProvider");
}
// Save the current configuration.
config.Save();
Console.WriteLine("Protected={0}",
section.SectionInformation.IsProtected);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}

Related

Visual Studio 2019 doesn't save the options of a form app [duplicate]

What I want to achieve is very simple: I have a Windows Forms (.NET 3.5) application that uses a path for reading information. This path can be modified by the user, by using the options form I provide.
Now, I want to save the path value to a file for later use. This would be one of the many settings saved to this file. This file would sit directly in the application folder.
I understand three options are available:
ConfigurationSettings file (appname.exe.config)
Registry
Custom XML file
I read that the .NET configuration file is not foreseen for saving values back to it. As for the registry, I would like to get as far away from it as possible.
Does this mean that I should use a custom XML file to save configuration settings?
If so, I would like to see code example of that (C#).
I have seen other discussions on this subject, but it is still not clear to me.
If you work with Visual Studio then it is pretty easy to get persistable settings. Right click on the project in Solution Explorer and choose Properties. Select the Settings tab and click on the hyperlink if settings doesn't exist.
Use the Settings tab to create application settings. Visual Studio creates the files Settings.settings and Settings.Designer.settings that contain the singleton class Settings inherited from ApplicationSettingsBase. You can access this class from your code to read/write application settings:
Properties.Settings.Default["SomeProperty"] = "Some Value";
Properties.Settings.Default.Save(); // Saves settings in application configuration file
This technique is applicable both for console, Windows Forms, and other project types.
Note that you need to set the scope property of your settings. If you select Application scope then Settings.Default.<your property> will be read-only.
Reference: How To: Write User Settings at Run Time with C# - Microsoft Docs
If you are planning on saving to a file within the same directory as your executable, here's a nice solution that uses the JSON format:
using System;
using System.IO;
using System.Web.Script.Serialization;
namespace MiscConsole
{
class Program
{
static void Main(string[] args)
{
MySettings settings = MySettings.Load();
Console.WriteLine("Current value of 'myInteger': " + settings.myInteger);
Console.WriteLine("Incrementing 'myInteger'...");
settings.myInteger++;
Console.WriteLine("Saving settings...");
settings.Save();
Console.WriteLine("Done.");
Console.ReadKey();
}
class MySettings : AppSettings<MySettings>
{
public string myString = "Hello World";
public int myInteger = 1;
}
}
public class AppSettings<T> where T : new()
{
private const string DEFAULT_FILENAME = "settings.json";
public void Save(string fileName = DEFAULT_FILENAME)
{
File.WriteAllText(fileName, (new JavaScriptSerializer()).Serialize(this));
}
public static void Save(T pSettings, string fileName = DEFAULT_FILENAME)
{
File.WriteAllText(fileName, (new JavaScriptSerializer()).Serialize(pSettings));
}
public static T Load(string fileName = DEFAULT_FILENAME)
{
T t = new T();
if(File.Exists(fileName))
t = (new JavaScriptSerializer()).Deserialize<T>(File.ReadAllText(fileName));
return t;
}
}
}
The registry is a no-go. You're not sure whether the user which uses your application, has sufficient rights to write to the registry.
You can use the app.config file to save application-level settings (that are the same for each user who uses your application).
I would store user-specific settings in an XML file, which would be saved in Isolated Storage or in the SpecialFolder.ApplicationData directory.
Next to that, as from .NET 2.0, it is possible to store values back to the app.config file.
The ApplicationSettings class doesn't support saving settings to the app.config file. That's very much by design; applications that run with a properly secured user account (think Vista UAC) do not have write access to the program's installation folder.
You can fight the system with the ConfigurationManager class. But the trivial workaround is to go into the Settings designer and change the setting's scope to User. If that causes hardships (say, the setting is relevant to every user), you should put your Options feature in a separate program so you can ask for the privilege elevation prompt. Or forego using a setting.
I wanted to share a library I've built for this. It's a tiny library, but a big improvement (IMHO) over .settings files.
The library is called Jot (GitHub). Here is an old The Code Project article I wrote about it.
Here's how you'd use it to keep track of a window's size and location:
public MainWindow()
{
InitializeComponent();
_stateTracker.Configure(this)
.IdentifyAs("MyMainWindow")
.AddProperties(nameof(Height), nameof(Width), nameof(Left), nameof(Top), nameof(WindowState))
.RegisterPersistTrigger(nameof(Closed))
.Apply();
}
The benefit compared to .settings files: There's considerably less code, and it's a lot less error-prone since you only need to mention each property once.
With a settings files you need to mention each property five times: once when you explicitly create the property and an additional four times in the code that copies the values back and forth.
Storage, serialization, etc. are completely configurable. When the target objects are created by an IoC container, you can [hook it up][] so that it applies tracking automatically to all objects it resolves, so that all you need to do to make a property persistent is slap a [Trackable] attribute on it.
It's highly configurable, and you can configure:
- when data is persisted and applied globally or for each tracked object
- how it's serialized
- where it's stored (e.g. file, database, online, isolated storage, registry)
- rules that can cancel applying/persisting data for a property
Trust me, the library is top notch!
The registry/configurationSettings/XML argument still seems very active. I've used them all, as the technology has progressed, but my favourite is based on Threed's system combined with Isolated Storage.
The following sample allows storage of an objects named properties to a file in isolated storage. Such as:
AppSettings.Save(myobject, "Prop1,Prop2", "myFile.jsn");
Properties may be recovered using:
AppSettings.Load(myobject, "myFile.jsn");
It is just a sample, not suggestive of best practices.
internal static class AppSettings
{
internal static void Save(object src, string targ, string fileName)
{
Dictionary<string, object> items = new Dictionary<string, object>();
Type type = src.GetType();
string[] paramList = targ.Split(new char[] { ',' });
foreach (string paramName in paramList)
items.Add(paramName, type.GetProperty(paramName.Trim()).GetValue(src, null));
try
{
// GetUserStoreForApplication doesn't work - can't identify.
// application unless published by ClickOnce or Silverlight
IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForAssembly();
using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(fileName, FileMode.Create, storage))
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write((new JavaScriptSerializer()).Serialize(items));
}
}
catch (Exception) { } // If fails - just don't use preferences
}
internal static void Load(object tar, string fileName)
{
Dictionary<string, object> items = new Dictionary<string, object>();
Type type = tar.GetType();
try
{
// GetUserStoreForApplication doesn't work - can't identify
// application unless published by ClickOnce or Silverlight
IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForAssembly();
using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(fileName, FileMode.Open, storage))
using (StreamReader reader = new StreamReader(stream))
{
items = (new JavaScriptSerializer()).Deserialize<Dictionary<string, object>>(reader.ReadToEnd());
}
}
catch (Exception) { return; } // If fails - just don't use preferences.
foreach (KeyValuePair<string, object> obj in items)
{
try
{
tar.GetType().GetProperty(obj.Key).SetValue(tar, obj.Value, null);
}
catch (Exception) { }
}
}
}
A simple way is to use a configuration data object, save it as an XML file with the name of the application in the local Folder and on startup read it back.
Here is an example to store the position and size of a form.
The configuration dataobject is strongly typed and easy to use:
[Serializable()]
public class CConfigDO
{
private System.Drawing.Point m_oStartPos;
private System.Drawing.Size m_oStartSize;
public System.Drawing.Point StartPos
{
get { return m_oStartPos; }
set { m_oStartPos = value; }
}
public System.Drawing.Size StartSize
{
get { return m_oStartSize; }
set { m_oStartSize = value; }
}
}
A manager class for saving and loading:
public class CConfigMng
{
private string m_sConfigFileName = System.IO.Path.GetFileNameWithoutExtension(System.Windows.Forms.Application.ExecutablePath) + ".xml";
private CConfigDO m_oConfig = new CConfigDO();
public CConfigDO Config
{
get { return m_oConfig; }
set { m_oConfig = value; }
}
// Load configuration file
public void LoadConfig()
{
if (System.IO.File.Exists(m_sConfigFileName))
{
System.IO.StreamReader srReader = System.IO.File.OpenText(m_sConfigFileName);
Type tType = m_oConfig.GetType();
System.Xml.Serialization.XmlSerializer xsSerializer = new System.Xml.Serialization.XmlSerializer(tType);
object oData = xsSerializer.Deserialize(srReader);
m_oConfig = (CConfigDO)oData;
srReader.Close();
}
}
// Save configuration file
public void SaveConfig()
{
System.IO.StreamWriter swWriter = System.IO.File.CreateText(m_sConfigFileName);
Type tType = m_oConfig.GetType();
if (tType.IsSerializable)
{
System.Xml.Serialization.XmlSerializer xsSerializer = new System.Xml.Serialization.XmlSerializer(tType);
xsSerializer.Serialize(swWriter, m_oConfig);
swWriter.Close();
}
}
}
Now you can create an instance and use in your form's load and close events:
private CConfigMng oConfigMng = new CConfigMng();
private void Form1_Load(object sender, EventArgs e)
{
// Load configuration
oConfigMng.LoadConfig();
if (oConfigMng.Config.StartPos.X != 0 || oConfigMng.Config.StartPos.Y != 0)
{
Location = oConfigMng.Config.StartPos;
Size = oConfigMng.Config.StartSize;
}
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
// Save configuration
oConfigMng.Config.StartPos = Location;
oConfigMng.Config.StartSize = Size;
oConfigMng.SaveConfig();
}
And the produced XML file is also readable:
<?xml version="1.0" encoding="utf-8"?>
<CConfigDO xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<StartPos>
<X>70</X>
<Y>278</Y>
</StartPos>
<StartSize>
<Width>253</Width>
<Height>229</Height>
</StartSize>
</CConfigDO>
Yes, it is possible to save the configuration - but it pretty much depends on the way you choose to do it. Let me describe the technical differences so you can understand the options you have:
First, you need to distinguish, whether you want to use applicationSettings or AppSettings in your *.exe.config(aka App.config in Visual Studio) file - there are fundamental differences, being described here.
Both provide different ways of saving changes:
The AppSettings allow you to read and write directly into the config file via config.Save(ConfigurationSaveMode.Modified);, where config is defined as: config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
The applicationSettings allow to read, but if you write changes (via Properties.Settings.Default.Save();) it will be written on a per-user basis, stored in a special place (e.g. C:\Documents and Settings\USERID\Local Settings\Application Data\FIRMNAME\WindowsFormsTestApplicati_Url_tdq2oylz33rzq00sxhvxucu5edw2oghw\1.0.0.0). As Hans Passant mentioned in his answer, this is because a user usually has restricted rights to Program Files and cannot write to it without invoking the UAC prompt. A disadvantage is if you're adding configuration keys in the future you need to synchronize them with every user profile.
But there are a couple of other alternative options:
Since .NET Core (and .NET 5 and 6) a 3rd option is the appsettings.json file which uses Microsoft's configuration abstraction (and also the secrets.json file which is stored in your user profile rather than in the assemblies directories). But usually WinForms isn't using it, so I am mentioning it just for completeness. However, here are some references how to read and write the values. Alternatively you can use Newtonsoft JSON to read and write the appsettings.json file, but it is not limited to that: you can also create your own json files with that method.
As mentioned in the question, there is a 4th option: If you treat the configuration file as XML document, you can load, modify and save it by using the System.Xml.Linq.XDocument class. It is not required to use a custom XML file, you can read the existing config file; for querying elements, you can even use Linq queries. I have given an example here, check out the function GetApplicationSetting there in the answer.
A 5th option is to store settings in the registry. How you can do it is described here.
Last not least, there is a 6th option: You can store values in the environment (system environment or environment of your account). In Windows settings (the cogwheel in the Windows menu), type in "environment" in the search bar and add or edit them there. To read them, use var myValue = Environment.GetEnvironmentVariable("MyVariable");. Note that your application usually needs to be restarted to get the updated environment settings.
If you require encryption to protect your values, check out this answer. It describes how to use Microsoft's DPAPI to store values encrypted.
And if you want to support your own files, whether XML or JSON, it might be useful to know the directory of the assembly running:
var assemblyDLL = System.Reflection.Assembly.GetExecutingAssembly();
var assemblyDirectory = System.IO.Path.GetDirectoryName(assemblyDLL.Location);
You can use assemblyDirectory as base directory to store your files.
I don't like the proposed solution of using web.config or app.config. Try reading your own XML. Have a look at XML Settings Files – No more web.config.
"Does this mean that I should use a custom XML file to save configuration settings?" No, not necessarily. We use SharpConfig for such operations.
For instance, if a configuration file is like that
[General]
# a comment
SomeString = Hello World!
SomeInteger = 10 # an inline comment
We can retrieve values like this
var config = Configuration.LoadFromFile("sample.cfg");
var section = config["General"];
string someString = section["SomeString"].StringValue;
int someInteger = section["SomeInteger"].IntValue;
It is compatible with .NET 2.0 and higher. We can create configuration files on the fly and we can save it later.
Source: http://sharpconfig.net/
GitHub: https://github.com/cemdervis/SharpConfig
Other options, instead of using a custom XML file, we can use a more user friendly file format: JSON or YAML file.
If you use .NET 4.0 dynamic, this library is really easy to use
(serialize, deserialize, nested objects support and ordering output
as you wish + merging multiple settings to one) JsonConfig (usage is equivalent to ApplicationSettingsBase)
For .NET YAML configuration library... I haven't found one that is as
easy to use as JsonConfig
You can store your settings file in multiple special folders (for all users and per user) as listed here Environment.SpecialFolder Enumeration and multiple files (default read only, per role, per user, etc.)
Sample for getting path of special folder: C# getting the path of
%AppData%
If you choose to use multiple settings, you can merge those settings: For example, merging settings for default + BasicUser + AdminUser. You can use your own rules: the last one overrides the value, etc.
As far as I can tell, .NET does support persisting settings using the built-in application settings facility:
The Application Settings feature of Windows Forms makes it easy to create, store, and maintain custom application and user preferences on the client computer. With Windows Forms application settings, you can store not only application data such as database connection strings, but also user-specific data, such as user application preferences. Using Visual Studio or custom managed code, you can create new settings, read them from and write them to disk, bind them to properties on your forms, and validate settings data prior to loading and saving.
- http://msdn.microsoft.com/en-us/library/k4s6c3a0.aspx
Sometimes you want to get rid of those settings kept in the traditional web.config or app.config file. You want more fine grained control over the deployment of your settings entries and separated data design. Or the requirement is to enable adding new entries at runtime.
I can imagine two good options:
The strongly typed version and
The object oriented version.
The advantage of the strongly typed version are the strongly typed settings names and values. There is no risk of intermixing names or data types. The disadvantage is that more settings have to be coded, cannot be added at runtime.
With the object oriented version the advantage is that new settings can be added at runtime. But you do not have strongly typed names and values. Must be careful with string identifiers. Must know data type saved earlier when getting a value.
You can find the code of both fully functional implementations HERE.
public static class SettingsExtensions
{
public static bool TryGetValue<T>(this Settings settings, string key, out T value)
{
if (settings.Properties[key] != null)
{
value = (T) settings[key];
return true;
}
value = default(T);
return false;
}
public static bool ContainsKey(this Settings settings, string key)
{
return settings.Properties[key] != null;
}
public static void SetValue<T>(this Settings settings, string key, T value)
{
if (settings.Properties[key] == null)
{
var p = new SettingsProperty(key)
{
PropertyType = typeof(T),
Provider = settings.Providers["LocalFileSettingsProvider"],
SerializeAs = SettingsSerializeAs.Xml
};
p.Attributes.Add(typeof(UserScopedSettingAttribute), new UserScopedSettingAttribute());
var v = new SettingsPropertyValue(p);
settings.Properties.Add(p);
settings.Reload();
}
settings[key] = value;
settings.Save();
}
}

How can I allow the user to modify connection strings stored in app.config if they lack write permissions?

First, some background on our app: When deployed, our application will be installed on a clients machine in the Program Files directory. We use connection strings which are stored in the App.config file that comes with the program. Under most circumstances, these connection strings will never change. However, at some point, it may be possible that the connection string info will go out of date. To that end, we have included a feature in our program that allows the user to enter new database information if the database can't be reached. The information is parsed into a connection string, tested for validity, and written back into the config file if valid. (Yes, this change is intended to affect all users on the computer, since they will be unable to run the application without a valid connection string anyway - if one user knows the DB info, then other users on the same computer will benefit from that knowledge. Writing to App.config instead of a per-user settings file is preferred.)
The problem I'm running into is that the end user will not have admin permissions, and thus will not be able to run our app at a level that allows it to make any changes to its own config file (since it is located in the C:\Program Files directory). I'm looking at a couple of different options, but I'm having problems implementing each of them:
Move config file to a different location. Not possible in this case because we are deploying an executable, and from what I understand, the App.config file must reside in the same directory.
Separate the connection string info into an external config file. I know about the configSource property that can be added to the connection string section of App.config. However, I'm having trouble specifying a concrete target. I can't simply put the file alongside the executable (otherwise I'd get the same permissions issues as the regular App.config). However, it appears environment variables (such as %AppData%) are not supported as valid config sources. I have tried to implement the solution from this question, but the program crashes before I can change the config source to a valid string because the ConfigurationManager apparently attempts to read the config source folder immediately and thus crashes with a FileNotFoundException.
Create our own config file implementation. I could always just create a custom class that is dedicated to reading a config file that is located in a specific location, but I would prefer using the built-in ConfigurationManager if possible.
My main question is this: How can I allow the end user (who only has read permissions in the application folder) to modify config settings dynamically when the config file must stay in the application folder?
Since windows xp, the OS prevents programs with out admin privileges from writing to the Program Files folder. The "Program Data" folder was created to allow programs to store persistent information. I always use a custom config file to store program data that an end User needs to config to connect to a database. I also encrypt at least the password of the connection string, but that is a another discussion. I provided a sample class and usage that stores a config file in Program Data Folder.
Config Class
using System;
using System.IO;
using System.Xml.Serialization;
namespace MyNamespace
{
public class Config
{
public string SomeStringProperty { get; set; }
public int SomeIntProperty { get; set; }
private static string _filePath = null;
static Config()
{
_filePath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.CommonApplicationData) + #"\My Program Name";
}
public static Config LoadConfig()
{
if (File.Exists(_filePath))
{
try
{
XmlSerializer reader = new XmlSerializer(typeof(Config));
StreamReader file = new StreamReader(_filePath);
Config config = (reader.Deserialize(file) as Config);
return config;
}
catch (Exception ex)
{
//Deal With no file file read error here.
}
}
// IF we get here no valid config file was loaded so make a new one.
return new Config();
}
public Exception SaveConfig()
{
try
{
XmlSerializer writer = new XmlSerializer(typeof(Config));
StreamWriter file = new StreamWriter(_filePath);
writer.Serialize(file, this);
file.Close();
return null;
}
catch (Exception ex)
{
return ex;
}
}
}
}
Basic Usage
Config config = Config.LoadConfig();
Debug.WriteLine("SomeIntProperty=" + config.SomeIntProperty.ToString());
Debug.WriteLine("SomeStringProperty=" + config.SomeStringProperty);
config.SomeIntProperty = 10;
config.SomeStringProperty = "Hello";
config.SaveConfig();
var configuration = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~");
var section = (ConnectionStringsSection)configuration.GetSection("connectionStrings");
section.ConnectionStrings["YourNameConnectionString"].ConnectionString = "yourstring";

Best practice for saving Persistent Settings in C# [duplicate]

What I want to achieve is very simple: I have a Windows Forms (.NET 3.5) application that uses a path for reading information. This path can be modified by the user, by using the options form I provide.
Now, I want to save the path value to a file for later use. This would be one of the many settings saved to this file. This file would sit directly in the application folder.
I understand three options are available:
ConfigurationSettings file (appname.exe.config)
Registry
Custom XML file
I read that the .NET configuration file is not foreseen for saving values back to it. As for the registry, I would like to get as far away from it as possible.
Does this mean that I should use a custom XML file to save configuration settings?
If so, I would like to see code example of that (C#).
I have seen other discussions on this subject, but it is still not clear to me.
If you work with Visual Studio then it is pretty easy to get persistable settings. Right click on the project in Solution Explorer and choose Properties. Select the Settings tab and click on the hyperlink if settings doesn't exist.
Use the Settings tab to create application settings. Visual Studio creates the files Settings.settings and Settings.Designer.settings that contain the singleton class Settings inherited from ApplicationSettingsBase. You can access this class from your code to read/write application settings:
Properties.Settings.Default["SomeProperty"] = "Some Value";
Properties.Settings.Default.Save(); // Saves settings in application configuration file
This technique is applicable both for console, Windows Forms, and other project types.
Note that you need to set the scope property of your settings. If you select Application scope then Settings.Default.<your property> will be read-only.
Reference: How To: Write User Settings at Run Time with C# - Microsoft Docs
If you are planning on saving to a file within the same directory as your executable, here's a nice solution that uses the JSON format:
using System;
using System.IO;
using System.Web.Script.Serialization;
namespace MiscConsole
{
class Program
{
static void Main(string[] args)
{
MySettings settings = MySettings.Load();
Console.WriteLine("Current value of 'myInteger': " + settings.myInteger);
Console.WriteLine("Incrementing 'myInteger'...");
settings.myInteger++;
Console.WriteLine("Saving settings...");
settings.Save();
Console.WriteLine("Done.");
Console.ReadKey();
}
class MySettings : AppSettings<MySettings>
{
public string myString = "Hello World";
public int myInteger = 1;
}
}
public class AppSettings<T> where T : new()
{
private const string DEFAULT_FILENAME = "settings.json";
public void Save(string fileName = DEFAULT_FILENAME)
{
File.WriteAllText(fileName, (new JavaScriptSerializer()).Serialize(this));
}
public static void Save(T pSettings, string fileName = DEFAULT_FILENAME)
{
File.WriteAllText(fileName, (new JavaScriptSerializer()).Serialize(pSettings));
}
public static T Load(string fileName = DEFAULT_FILENAME)
{
T t = new T();
if(File.Exists(fileName))
t = (new JavaScriptSerializer()).Deserialize<T>(File.ReadAllText(fileName));
return t;
}
}
}
The registry is a no-go. You're not sure whether the user which uses your application, has sufficient rights to write to the registry.
You can use the app.config file to save application-level settings (that are the same for each user who uses your application).
I would store user-specific settings in an XML file, which would be saved in Isolated Storage or in the SpecialFolder.ApplicationData directory.
Next to that, as from .NET 2.0, it is possible to store values back to the app.config file.
The ApplicationSettings class doesn't support saving settings to the app.config file. That's very much by design; applications that run with a properly secured user account (think Vista UAC) do not have write access to the program's installation folder.
You can fight the system with the ConfigurationManager class. But the trivial workaround is to go into the Settings designer and change the setting's scope to User. If that causes hardships (say, the setting is relevant to every user), you should put your Options feature in a separate program so you can ask for the privilege elevation prompt. Or forego using a setting.
I wanted to share a library I've built for this. It's a tiny library, but a big improvement (IMHO) over .settings files.
The library is called Jot (GitHub). Here is an old The Code Project article I wrote about it.
Here's how you'd use it to keep track of a window's size and location:
public MainWindow()
{
InitializeComponent();
_stateTracker.Configure(this)
.IdentifyAs("MyMainWindow")
.AddProperties(nameof(Height), nameof(Width), nameof(Left), nameof(Top), nameof(WindowState))
.RegisterPersistTrigger(nameof(Closed))
.Apply();
}
The benefit compared to .settings files: There's considerably less code, and it's a lot less error-prone since you only need to mention each property once.
With a settings files you need to mention each property five times: once when you explicitly create the property and an additional four times in the code that copies the values back and forth.
Storage, serialization, etc. are completely configurable. When the target objects are created by an IoC container, you can [hook it up][] so that it applies tracking automatically to all objects it resolves, so that all you need to do to make a property persistent is slap a [Trackable] attribute on it.
It's highly configurable, and you can configure:
- when data is persisted and applied globally or for each tracked object
- how it's serialized
- where it's stored (e.g. file, database, online, isolated storage, registry)
- rules that can cancel applying/persisting data for a property
Trust me, the library is top notch!
The registry/configurationSettings/XML argument still seems very active. I've used them all, as the technology has progressed, but my favourite is based on Threed's system combined with Isolated Storage.
The following sample allows storage of an objects named properties to a file in isolated storage. Such as:
AppSettings.Save(myobject, "Prop1,Prop2", "myFile.jsn");
Properties may be recovered using:
AppSettings.Load(myobject, "myFile.jsn");
It is just a sample, not suggestive of best practices.
internal static class AppSettings
{
internal static void Save(object src, string targ, string fileName)
{
Dictionary<string, object> items = new Dictionary<string, object>();
Type type = src.GetType();
string[] paramList = targ.Split(new char[] { ',' });
foreach (string paramName in paramList)
items.Add(paramName, type.GetProperty(paramName.Trim()).GetValue(src, null));
try
{
// GetUserStoreForApplication doesn't work - can't identify.
// application unless published by ClickOnce or Silverlight
IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForAssembly();
using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(fileName, FileMode.Create, storage))
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write((new JavaScriptSerializer()).Serialize(items));
}
}
catch (Exception) { } // If fails - just don't use preferences
}
internal static void Load(object tar, string fileName)
{
Dictionary<string, object> items = new Dictionary<string, object>();
Type type = tar.GetType();
try
{
// GetUserStoreForApplication doesn't work - can't identify
// application unless published by ClickOnce or Silverlight
IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForAssembly();
using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(fileName, FileMode.Open, storage))
using (StreamReader reader = new StreamReader(stream))
{
items = (new JavaScriptSerializer()).Deserialize<Dictionary<string, object>>(reader.ReadToEnd());
}
}
catch (Exception) { return; } // If fails - just don't use preferences.
foreach (KeyValuePair<string, object> obj in items)
{
try
{
tar.GetType().GetProperty(obj.Key).SetValue(tar, obj.Value, null);
}
catch (Exception) { }
}
}
}
A simple way is to use a configuration data object, save it as an XML file with the name of the application in the local Folder and on startup read it back.
Here is an example to store the position and size of a form.
The configuration dataobject is strongly typed and easy to use:
[Serializable()]
public class CConfigDO
{
private System.Drawing.Point m_oStartPos;
private System.Drawing.Size m_oStartSize;
public System.Drawing.Point StartPos
{
get { return m_oStartPos; }
set { m_oStartPos = value; }
}
public System.Drawing.Size StartSize
{
get { return m_oStartSize; }
set { m_oStartSize = value; }
}
}
A manager class for saving and loading:
public class CConfigMng
{
private string m_sConfigFileName = System.IO.Path.GetFileNameWithoutExtension(System.Windows.Forms.Application.ExecutablePath) + ".xml";
private CConfigDO m_oConfig = new CConfigDO();
public CConfigDO Config
{
get { return m_oConfig; }
set { m_oConfig = value; }
}
// Load configuration file
public void LoadConfig()
{
if (System.IO.File.Exists(m_sConfigFileName))
{
System.IO.StreamReader srReader = System.IO.File.OpenText(m_sConfigFileName);
Type tType = m_oConfig.GetType();
System.Xml.Serialization.XmlSerializer xsSerializer = new System.Xml.Serialization.XmlSerializer(tType);
object oData = xsSerializer.Deserialize(srReader);
m_oConfig = (CConfigDO)oData;
srReader.Close();
}
}
// Save configuration file
public void SaveConfig()
{
System.IO.StreamWriter swWriter = System.IO.File.CreateText(m_sConfigFileName);
Type tType = m_oConfig.GetType();
if (tType.IsSerializable)
{
System.Xml.Serialization.XmlSerializer xsSerializer = new System.Xml.Serialization.XmlSerializer(tType);
xsSerializer.Serialize(swWriter, m_oConfig);
swWriter.Close();
}
}
}
Now you can create an instance and use in your form's load and close events:
private CConfigMng oConfigMng = new CConfigMng();
private void Form1_Load(object sender, EventArgs e)
{
// Load configuration
oConfigMng.LoadConfig();
if (oConfigMng.Config.StartPos.X != 0 || oConfigMng.Config.StartPos.Y != 0)
{
Location = oConfigMng.Config.StartPos;
Size = oConfigMng.Config.StartSize;
}
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
// Save configuration
oConfigMng.Config.StartPos = Location;
oConfigMng.Config.StartSize = Size;
oConfigMng.SaveConfig();
}
And the produced XML file is also readable:
<?xml version="1.0" encoding="utf-8"?>
<CConfigDO xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<StartPos>
<X>70</X>
<Y>278</Y>
</StartPos>
<StartSize>
<Width>253</Width>
<Height>229</Height>
</StartSize>
</CConfigDO>
Yes, it is possible to save the configuration - but it pretty much depends on the way you choose to do it. Let me describe the technical differences so you can understand the options you have:
First, you need to distinguish, whether you want to use applicationSettings or AppSettings in your *.exe.config(aka App.config in Visual Studio) file - there are fundamental differences, being described here.
Both provide different ways of saving changes:
The AppSettings allow you to read and write directly into the config file via config.Save(ConfigurationSaveMode.Modified);, where config is defined as: config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
The applicationSettings allow to read, but if you write changes (via Properties.Settings.Default.Save();) it will be written on a per-user basis, stored in a special place (e.g. C:\Documents and Settings\USERID\Local Settings\Application Data\FIRMNAME\WindowsFormsTestApplicati_Url_tdq2oylz33rzq00sxhvxucu5edw2oghw\1.0.0.0). As Hans Passant mentioned in his answer, this is because a user usually has restricted rights to Program Files and cannot write to it without invoking the UAC prompt. A disadvantage is if you're adding configuration keys in the future you need to synchronize them with every user profile.
But there are a couple of other alternative options:
Since .NET Core (and .NET 5 and 6) a 3rd option is the appsettings.json file which uses Microsoft's configuration abstraction (and also the secrets.json file which is stored in your user profile rather than in the assemblies directories). But usually WinForms isn't using it, so I am mentioning it just for completeness. However, here are some references how to read and write the values. Alternatively you can use Newtonsoft JSON to read and write the appsettings.json file, but it is not limited to that: you can also create your own json files with that method.
As mentioned in the question, there is a 4th option: If you treat the configuration file as XML document, you can load, modify and save it by using the System.Xml.Linq.XDocument class. It is not required to use a custom XML file, you can read the existing config file; for querying elements, you can even use Linq queries. I have given an example here, check out the function GetApplicationSetting there in the answer.
A 5th option is to store settings in the registry. How you can do it is described here.
Last not least, there is a 6th option: You can store values in the environment (system environment or environment of your account). In Windows settings (the cogwheel in the Windows menu), type in "environment" in the search bar and add or edit them there. To read them, use var myValue = Environment.GetEnvironmentVariable("MyVariable");. Note that your application usually needs to be restarted to get the updated environment settings.
If you require encryption to protect your values, check out this answer. It describes how to use Microsoft's DPAPI to store values encrypted.
And if you want to support your own files, whether XML or JSON, it might be useful to know the directory of the assembly running:
var assemblyDLL = System.Reflection.Assembly.GetExecutingAssembly();
var assemblyDirectory = System.IO.Path.GetDirectoryName(assemblyDLL.Location);
You can use assemblyDirectory as base directory to store your files.
I don't like the proposed solution of using web.config or app.config. Try reading your own XML. Have a look at XML Settings Files – No more web.config.
"Does this mean that I should use a custom XML file to save configuration settings?" No, not necessarily. We use SharpConfig for such operations.
For instance, if a configuration file is like that
[General]
# a comment
SomeString = Hello World!
SomeInteger = 10 # an inline comment
We can retrieve values like this
var config = Configuration.LoadFromFile("sample.cfg");
var section = config["General"];
string someString = section["SomeString"].StringValue;
int someInteger = section["SomeInteger"].IntValue;
It is compatible with .NET 2.0 and higher. We can create configuration files on the fly and we can save it later.
Source: http://sharpconfig.net/
GitHub: https://github.com/cemdervis/SharpConfig
Other options, instead of using a custom XML file, we can use a more user friendly file format: JSON or YAML file.
If you use .NET 4.0 dynamic, this library is really easy to use
(serialize, deserialize, nested objects support and ordering output
as you wish + merging multiple settings to one) JsonConfig (usage is equivalent to ApplicationSettingsBase)
For .NET YAML configuration library... I haven't found one that is as
easy to use as JsonConfig
You can store your settings file in multiple special folders (for all users and per user) as listed here Environment.SpecialFolder Enumeration and multiple files (default read only, per role, per user, etc.)
Sample for getting path of special folder: C# getting the path of
%AppData%
If you choose to use multiple settings, you can merge those settings: For example, merging settings for default + BasicUser + AdminUser. You can use your own rules: the last one overrides the value, etc.
As far as I can tell, .NET does support persisting settings using the built-in application settings facility:
The Application Settings feature of Windows Forms makes it easy to create, store, and maintain custom application and user preferences on the client computer. With Windows Forms application settings, you can store not only application data such as database connection strings, but also user-specific data, such as user application preferences. Using Visual Studio or custom managed code, you can create new settings, read them from and write them to disk, bind them to properties on your forms, and validate settings data prior to loading and saving.
- http://msdn.microsoft.com/en-us/library/k4s6c3a0.aspx
Sometimes you want to get rid of those settings kept in the traditional web.config or app.config file. You want more fine grained control over the deployment of your settings entries and separated data design. Or the requirement is to enable adding new entries at runtime.
I can imagine two good options:
The strongly typed version and
The object oriented version.
The advantage of the strongly typed version are the strongly typed settings names and values. There is no risk of intermixing names or data types. The disadvantage is that more settings have to be coded, cannot be added at runtime.
With the object oriented version the advantage is that new settings can be added at runtime. But you do not have strongly typed names and values. Must be careful with string identifiers. Must know data type saved earlier when getting a value.
You can find the code of both fully functional implementations HERE.
public static class SettingsExtensions
{
public static bool TryGetValue<T>(this Settings settings, string key, out T value)
{
if (settings.Properties[key] != null)
{
value = (T) settings[key];
return true;
}
value = default(T);
return false;
}
public static bool ContainsKey(this Settings settings, string key)
{
return settings.Properties[key] != null;
}
public static void SetValue<T>(this Settings settings, string key, T value)
{
if (settings.Properties[key] == null)
{
var p = new SettingsProperty(key)
{
PropertyType = typeof(T),
Provider = settings.Providers["LocalFileSettingsProvider"],
SerializeAs = SettingsSerializeAs.Xml
};
p.Attributes.Add(typeof(UserScopedSettingAttribute), new UserScopedSettingAttribute());
var v = new SettingsPropertyValue(p);
settings.Properties.Add(p);
settings.Reload();
}
settings[key] = value;
settings.Save();
}
}

Overwrite encrypted configuration section without decrypting?

When I try to access configuration section that is encrypted and cannot be decrypted properly (for example, someone just grabbed config file from another machine blindly) - Configuration class is throwing an exception. I want to catch that situation and rewrite the section completely in such case.
I've tried to remove and add back the section in question, but it seems that removal is ignored - second statement in 'catch' throws another exception about such section already exists:
try
{
// this getter might throw an exception - e.g. if encrypted on another machine
var connStrings = config.ConnectionStrings;
}
catch (ConfigurationException)
{
config.Sections.Remove("connectionStrings");
config.Sections.Add("connectionStrings", new ConnectionStringsSection());
}
It might be related to the fact I have connectionStrings section residing in separate file, i.e. my config file has something like <connectionStrings configSource="connections.config"/>, while actual encrypted content is in the connections.config file.
Is it possible to do what I need without falling back to direct XML manipulations, by using .NET Configuration classes only?
I'm pretty sure this will do what you want. In my case, I just included a bogus connectionString setting:
<connectionStrings>
<add connectionString="foo"/>
</connectionStrings>
I didn't include a "name" property, so trying to read ConnectionStrings will blow up, just like in your case:
try {
var x = ConfigurationManager.ConnectionStrings; //blows up
}
catch {
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
config.Sections.Remove("connectionStrings");
config.Save(); //Now save the changes
ConfigurationManager.RefreshSection("connectionStrings"); //and reload the section
var x = ConfigurationManager.ConnectionStrings; //can now read!
}
You still won't be able to do this:
config.Sections.Add("connectionStrings", new ConnectionStringsSection());
Because (at least on my machine), it's picking up connectionStrings from the Machine.config.
I suspect though that in your case that's fine. You are now at a point where if you want to, you can add your own connection strings and you don't really need to completely blow away the connection string section.

How do I add appSettings from C# code? [duplicate]

What I want to achieve is very simple: I have a Windows Forms (.NET 3.5) application that uses a path for reading information. This path can be modified by the user, by using the options form I provide.
Now, I want to save the path value to a file for later use. This would be one of the many settings saved to this file. This file would sit directly in the application folder.
I understand three options are available:
ConfigurationSettings file (appname.exe.config)
Registry
Custom XML file
I read that the .NET configuration file is not foreseen for saving values back to it. As for the registry, I would like to get as far away from it as possible.
Does this mean that I should use a custom XML file to save configuration settings?
If so, I would like to see code example of that (C#).
I have seen other discussions on this subject, but it is still not clear to me.
If you work with Visual Studio then it is pretty easy to get persistable settings. Right click on the project in Solution Explorer and choose Properties. Select the Settings tab and click on the hyperlink if settings doesn't exist.
Use the Settings tab to create application settings. Visual Studio creates the files Settings.settings and Settings.Designer.settings that contain the singleton class Settings inherited from ApplicationSettingsBase. You can access this class from your code to read/write application settings:
Properties.Settings.Default["SomeProperty"] = "Some Value";
Properties.Settings.Default.Save(); // Saves settings in application configuration file
This technique is applicable both for console, Windows Forms, and other project types.
Note that you need to set the scope property of your settings. If you select Application scope then Settings.Default.<your property> will be read-only.
Reference: How To: Write User Settings at Run Time with C# - Microsoft Docs
If you are planning on saving to a file within the same directory as your executable, here's a nice solution that uses the JSON format:
using System;
using System.IO;
using System.Web.Script.Serialization;
namespace MiscConsole
{
class Program
{
static void Main(string[] args)
{
MySettings settings = MySettings.Load();
Console.WriteLine("Current value of 'myInteger': " + settings.myInteger);
Console.WriteLine("Incrementing 'myInteger'...");
settings.myInteger++;
Console.WriteLine("Saving settings...");
settings.Save();
Console.WriteLine("Done.");
Console.ReadKey();
}
class MySettings : AppSettings<MySettings>
{
public string myString = "Hello World";
public int myInteger = 1;
}
}
public class AppSettings<T> where T : new()
{
private const string DEFAULT_FILENAME = "settings.json";
public void Save(string fileName = DEFAULT_FILENAME)
{
File.WriteAllText(fileName, (new JavaScriptSerializer()).Serialize(this));
}
public static void Save(T pSettings, string fileName = DEFAULT_FILENAME)
{
File.WriteAllText(fileName, (new JavaScriptSerializer()).Serialize(pSettings));
}
public static T Load(string fileName = DEFAULT_FILENAME)
{
T t = new T();
if(File.Exists(fileName))
t = (new JavaScriptSerializer()).Deserialize<T>(File.ReadAllText(fileName));
return t;
}
}
}
The registry is a no-go. You're not sure whether the user which uses your application, has sufficient rights to write to the registry.
You can use the app.config file to save application-level settings (that are the same for each user who uses your application).
I would store user-specific settings in an XML file, which would be saved in Isolated Storage or in the SpecialFolder.ApplicationData directory.
Next to that, as from .NET 2.0, it is possible to store values back to the app.config file.
The ApplicationSettings class doesn't support saving settings to the app.config file. That's very much by design; applications that run with a properly secured user account (think Vista UAC) do not have write access to the program's installation folder.
You can fight the system with the ConfigurationManager class. But the trivial workaround is to go into the Settings designer and change the setting's scope to User. If that causes hardships (say, the setting is relevant to every user), you should put your Options feature in a separate program so you can ask for the privilege elevation prompt. Or forego using a setting.
I wanted to share a library I've built for this. It's a tiny library, but a big improvement (IMHO) over .settings files.
The library is called Jot (GitHub). Here is an old The Code Project article I wrote about it.
Here's how you'd use it to keep track of a window's size and location:
public MainWindow()
{
InitializeComponent();
_stateTracker.Configure(this)
.IdentifyAs("MyMainWindow")
.AddProperties(nameof(Height), nameof(Width), nameof(Left), nameof(Top), nameof(WindowState))
.RegisterPersistTrigger(nameof(Closed))
.Apply();
}
The benefit compared to .settings files: There's considerably less code, and it's a lot less error-prone since you only need to mention each property once.
With a settings files you need to mention each property five times: once when you explicitly create the property and an additional four times in the code that copies the values back and forth.
Storage, serialization, etc. are completely configurable. When the target objects are created by an IoC container, you can [hook it up][] so that it applies tracking automatically to all objects it resolves, so that all you need to do to make a property persistent is slap a [Trackable] attribute on it.
It's highly configurable, and you can configure:
- when data is persisted and applied globally or for each tracked object
- how it's serialized
- where it's stored (e.g. file, database, online, isolated storage, registry)
- rules that can cancel applying/persisting data for a property
Trust me, the library is top notch!
The registry/configurationSettings/XML argument still seems very active. I've used them all, as the technology has progressed, but my favourite is based on Threed's system combined with Isolated Storage.
The following sample allows storage of an objects named properties to a file in isolated storage. Such as:
AppSettings.Save(myobject, "Prop1,Prop2", "myFile.jsn");
Properties may be recovered using:
AppSettings.Load(myobject, "myFile.jsn");
It is just a sample, not suggestive of best practices.
internal static class AppSettings
{
internal static void Save(object src, string targ, string fileName)
{
Dictionary<string, object> items = new Dictionary<string, object>();
Type type = src.GetType();
string[] paramList = targ.Split(new char[] { ',' });
foreach (string paramName in paramList)
items.Add(paramName, type.GetProperty(paramName.Trim()).GetValue(src, null));
try
{
// GetUserStoreForApplication doesn't work - can't identify.
// application unless published by ClickOnce or Silverlight
IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForAssembly();
using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(fileName, FileMode.Create, storage))
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write((new JavaScriptSerializer()).Serialize(items));
}
}
catch (Exception) { } // If fails - just don't use preferences
}
internal static void Load(object tar, string fileName)
{
Dictionary<string, object> items = new Dictionary<string, object>();
Type type = tar.GetType();
try
{
// GetUserStoreForApplication doesn't work - can't identify
// application unless published by ClickOnce or Silverlight
IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForAssembly();
using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(fileName, FileMode.Open, storage))
using (StreamReader reader = new StreamReader(stream))
{
items = (new JavaScriptSerializer()).Deserialize<Dictionary<string, object>>(reader.ReadToEnd());
}
}
catch (Exception) { return; } // If fails - just don't use preferences.
foreach (KeyValuePair<string, object> obj in items)
{
try
{
tar.GetType().GetProperty(obj.Key).SetValue(tar, obj.Value, null);
}
catch (Exception) { }
}
}
}
A simple way is to use a configuration data object, save it as an XML file with the name of the application in the local Folder and on startup read it back.
Here is an example to store the position and size of a form.
The configuration dataobject is strongly typed and easy to use:
[Serializable()]
public class CConfigDO
{
private System.Drawing.Point m_oStartPos;
private System.Drawing.Size m_oStartSize;
public System.Drawing.Point StartPos
{
get { return m_oStartPos; }
set { m_oStartPos = value; }
}
public System.Drawing.Size StartSize
{
get { return m_oStartSize; }
set { m_oStartSize = value; }
}
}
A manager class for saving and loading:
public class CConfigMng
{
private string m_sConfigFileName = System.IO.Path.GetFileNameWithoutExtension(System.Windows.Forms.Application.ExecutablePath) + ".xml";
private CConfigDO m_oConfig = new CConfigDO();
public CConfigDO Config
{
get { return m_oConfig; }
set { m_oConfig = value; }
}
// Load configuration file
public void LoadConfig()
{
if (System.IO.File.Exists(m_sConfigFileName))
{
System.IO.StreamReader srReader = System.IO.File.OpenText(m_sConfigFileName);
Type tType = m_oConfig.GetType();
System.Xml.Serialization.XmlSerializer xsSerializer = new System.Xml.Serialization.XmlSerializer(tType);
object oData = xsSerializer.Deserialize(srReader);
m_oConfig = (CConfigDO)oData;
srReader.Close();
}
}
// Save configuration file
public void SaveConfig()
{
System.IO.StreamWriter swWriter = System.IO.File.CreateText(m_sConfigFileName);
Type tType = m_oConfig.GetType();
if (tType.IsSerializable)
{
System.Xml.Serialization.XmlSerializer xsSerializer = new System.Xml.Serialization.XmlSerializer(tType);
xsSerializer.Serialize(swWriter, m_oConfig);
swWriter.Close();
}
}
}
Now you can create an instance and use in your form's load and close events:
private CConfigMng oConfigMng = new CConfigMng();
private void Form1_Load(object sender, EventArgs e)
{
// Load configuration
oConfigMng.LoadConfig();
if (oConfigMng.Config.StartPos.X != 0 || oConfigMng.Config.StartPos.Y != 0)
{
Location = oConfigMng.Config.StartPos;
Size = oConfigMng.Config.StartSize;
}
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
// Save configuration
oConfigMng.Config.StartPos = Location;
oConfigMng.Config.StartSize = Size;
oConfigMng.SaveConfig();
}
And the produced XML file is also readable:
<?xml version="1.0" encoding="utf-8"?>
<CConfigDO xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<StartPos>
<X>70</X>
<Y>278</Y>
</StartPos>
<StartSize>
<Width>253</Width>
<Height>229</Height>
</StartSize>
</CConfigDO>
Yes, it is possible to save the configuration - but it pretty much depends on the way you choose to do it. Let me describe the technical differences so you can understand the options you have:
First, you need to distinguish, whether you want to use applicationSettings or AppSettings in your *.exe.config(aka App.config in Visual Studio) file - there are fundamental differences, being described here.
Both provide different ways of saving changes:
The AppSettings allow you to read and write directly into the config file via config.Save(ConfigurationSaveMode.Modified);, where config is defined as: config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
The applicationSettings allow to read, but if you write changes (via Properties.Settings.Default.Save();) it will be written on a per-user basis, stored in a special place (e.g. C:\Documents and Settings\USERID\Local Settings\Application Data\FIRMNAME\WindowsFormsTestApplicati_Url_tdq2oylz33rzq00sxhvxucu5edw2oghw\1.0.0.0). As Hans Passant mentioned in his answer, this is because a user usually has restricted rights to Program Files and cannot write to it without invoking the UAC prompt. A disadvantage is if you're adding configuration keys in the future you need to synchronize them with every user profile.
But there are a couple of other alternative options:
Since .NET Core (and .NET 5 and 6) a 3rd option is the appsettings.json file which uses Microsoft's configuration abstraction (and also the secrets.json file which is stored in your user profile rather than in the assemblies directories). But usually WinForms isn't using it, so I am mentioning it just for completeness. However, here are some references how to read and write the values. Alternatively you can use Newtonsoft JSON to read and write the appsettings.json file, but it is not limited to that: you can also create your own json files with that method.
As mentioned in the question, there is a 4th option: If you treat the configuration file as XML document, you can load, modify and save it by using the System.Xml.Linq.XDocument class. It is not required to use a custom XML file, you can read the existing config file; for querying elements, you can even use Linq queries. I have given an example here, check out the function GetApplicationSetting there in the answer.
A 5th option is to store settings in the registry. How you can do it is described here.
Last not least, there is a 6th option: You can store values in the environment (system environment or environment of your account). In Windows settings (the cogwheel in the Windows menu), type in "environment" in the search bar and add or edit them there. To read them, use var myValue = Environment.GetEnvironmentVariable("MyVariable");. Note that your application usually needs to be restarted to get the updated environment settings.
If you require encryption to protect your values, check out this answer. It describes how to use Microsoft's DPAPI to store values encrypted.
And if you want to support your own files, whether XML or JSON, it might be useful to know the directory of the assembly running:
var assemblyDLL = System.Reflection.Assembly.GetExecutingAssembly();
var assemblyDirectory = System.IO.Path.GetDirectoryName(assemblyDLL.Location);
You can use assemblyDirectory as base directory to store your files.
I don't like the proposed solution of using web.config or app.config. Try reading your own XML. Have a look at XML Settings Files – No more web.config.
"Does this mean that I should use a custom XML file to save configuration settings?" No, not necessarily. We use SharpConfig for such operations.
For instance, if a configuration file is like that
[General]
# a comment
SomeString = Hello World!
SomeInteger = 10 # an inline comment
We can retrieve values like this
var config = Configuration.LoadFromFile("sample.cfg");
var section = config["General"];
string someString = section["SomeString"].StringValue;
int someInteger = section["SomeInteger"].IntValue;
It is compatible with .NET 2.0 and higher. We can create configuration files on the fly and we can save it later.
Source: http://sharpconfig.net/
GitHub: https://github.com/cemdervis/SharpConfig
Other options, instead of using a custom XML file, we can use a more user friendly file format: JSON or YAML file.
If you use .NET 4.0 dynamic, this library is really easy to use
(serialize, deserialize, nested objects support and ordering output
as you wish + merging multiple settings to one) JsonConfig (usage is equivalent to ApplicationSettingsBase)
For .NET YAML configuration library... I haven't found one that is as
easy to use as JsonConfig
You can store your settings file in multiple special folders (for all users and per user) as listed here Environment.SpecialFolder Enumeration and multiple files (default read only, per role, per user, etc.)
Sample for getting path of special folder: C# getting the path of
%AppData%
If you choose to use multiple settings, you can merge those settings: For example, merging settings for default + BasicUser + AdminUser. You can use your own rules: the last one overrides the value, etc.
As far as I can tell, .NET does support persisting settings using the built-in application settings facility:
The Application Settings feature of Windows Forms makes it easy to create, store, and maintain custom application and user preferences on the client computer. With Windows Forms application settings, you can store not only application data such as database connection strings, but also user-specific data, such as user application preferences. Using Visual Studio or custom managed code, you can create new settings, read them from and write them to disk, bind them to properties on your forms, and validate settings data prior to loading and saving.
- http://msdn.microsoft.com/en-us/library/k4s6c3a0.aspx
Sometimes you want to get rid of those settings kept in the traditional web.config or app.config file. You want more fine grained control over the deployment of your settings entries and separated data design. Or the requirement is to enable adding new entries at runtime.
I can imagine two good options:
The strongly typed version and
The object oriented version.
The advantage of the strongly typed version are the strongly typed settings names and values. There is no risk of intermixing names or data types. The disadvantage is that more settings have to be coded, cannot be added at runtime.
With the object oriented version the advantage is that new settings can be added at runtime. But you do not have strongly typed names and values. Must be careful with string identifiers. Must know data type saved earlier when getting a value.
You can find the code of both fully functional implementations HERE.
public static class SettingsExtensions
{
public static bool TryGetValue<T>(this Settings settings, string key, out T value)
{
if (settings.Properties[key] != null)
{
value = (T) settings[key];
return true;
}
value = default(T);
return false;
}
public static bool ContainsKey(this Settings settings, string key)
{
return settings.Properties[key] != null;
}
public static void SetValue<T>(this Settings settings, string key, T value)
{
if (settings.Properties[key] == null)
{
var p = new SettingsProperty(key)
{
PropertyType = typeof(T),
Provider = settings.Providers["LocalFileSettingsProvider"],
SerializeAs = SettingsSerializeAs.Xml
};
p.Attributes.Add(typeof(UserScopedSettingAttribute), new UserScopedSettingAttribute());
var v = new SettingsPropertyValue(p);
settings.Properties.Add(p);
settings.Reload();
}
settings[key] = value;
settings.Save();
}
}

Categories

Resources