I have a program which I'm going to deploy using the package for deployment built into VS.
Now I have an app.config file which I want the user to be able to modify (even after running the program), but I have no idea where exactly the installer dumps the files, and I'm not expecting the users to root around their filesystem.
What I was thinking is - I ask the user to specify some directory (which needs to happen anyway since its a game) - I check for the config file there, and if its not there, I copy it from the root directory the program can see - then read the one in the 'save' folder.
That said, its sounding like a very ugly and hacky solution - is there a better one?
I wouldn't be encouraging users to modify the app.config. The app.config is copied into the same directory as your app exe and usually contains settings which your app depends on to run correctly e.g. DB connection strings, system defaults etc. Your playing a dangerous game by letting users change settings in there directly.
A safer approach would be to export another XML file into the users documents folder where they can override app settings. Have your app load in the app.config first then override those values with any settings found in the users own config file.
I would also recommend implementing some sort of UI for this, even if its really basic. Your average user isn't going to be comfortable editing XML directly, too much margin for error.
You could use the application (or user) settings to persist any changes that the user might want to make in the configuration. See the ApplicationSettingsBase class and this article.
Say your application contains a user setting called Score, managed internally by a class called MyUserSettings:
public class MyUserSettings : ApplicationSettingsBase
{
[UserScopedSetting()]
[DefaultSettingValue(0)]
public int Rank
{
get
{
return (int)this["Score"];
}
set
{
this["Score"] = value;
}
}
}
You can save the current values, usually when the main form is closing, using the Save method:
myUserSettings.Save();
If you want users to modify some settings directly, you can use a property grid or your own form that binds the instance of MyUserSettings class.
If the settings are marked as "User", then the values would be stored in the user.config file, in %InstallRoot%\Documents and Settings\username\Local Settings or %InstallRoot%\Documents and Settings\username\Application Data (for roaming profiles).
Related
I have a C# program with various setting stored in the program project settings. As it is a command line program, these settings will be changed by the user in the user.config file. I can't work out how to force it to save all the default values.
Calling Properties.Settings.Default.Save(); only seems to work when I update a property in the program, which defeats the point of a persistent settings file.
If I run
Properties.Settings.Default.SomeSetting = "Help";
Properties.Settings.Default.Save();
In the config file I will see something like
< setting name="SomeSetting" serializeAs="String">
<value>Help</value>
< /setting>
What I want to happens is if the config file doesn't exist, create it and store all the settings. Then the user can change them if needed.
The way I managed to solve it was by modifying some existing code I had in a different project to handle version upgrades.
When the program runs:
if (PropertiesSettings.Default.UpgradeRequired)
{
Properties.Settings.Default.Upgrade();
Properties.Settings.Default.UpgradeRequired = false;
Properties.Settings.Default.Save();
reloadSettings();
}
// Assign settings to themselves and save
// This keeps settings the same as the defaults even when upgrading but ensures all settings appear in the config file
private void reloadSettings()
{
Properties.Settings.Default.Setting1 = Properties.Settings.Default.Setting1;
Properties.Settings.Default.Setting2 = Properties.Settings.Default.Setting2;
//etc
Properties.Settings.Default.Save();
}
The UpgradeRequired setting should be set to True by default, so a new version with no config will be forced to run the first if statement. The .NET Properties.Settings.Default.Upgrade(); should then take care of finding old config files and loading the old settings.
Settings are saved somewhere in user AppData folder specifically for a given process. For example, when the application is run under the debugger, you get different settings than when running stand-alone.
I would guess that there is no easy way to share settings across application using those properties settings. That code is quite old (.NET 2.0 or even before) so I think it is not designed to be easily customizable as it is often the case with more recent code.
I think that settings are good for things that are specific to a given application and if data need to be shared across application, then you should manage your own files.
Usually, one would use either XML or JSON based file and the data would be stored (by default) either under a subdirectory of My Documents if the data is intended to be visible to users or under a subdirectory of user application data directory if a regular user should not see those files.
I need some setting of an application that will be shared among all users of the computer, but could also be changed at at run time. That seam simple, but according to the Application Settings MSDN article, it's either one or the other.
There are two types of application settings, based on scope:
Application-scoped settings can be used for information such as a URL for a Web service or a database connection string. These values are associated with the application. Therefore, users cannot change them at run time.
User-scoped settings can be used for information such as persisting the last position of a form or a font preference. Users can change these values at run time.
I could write code to edit the app.config XML file, but since it's located in the program directory, it's protected under windows 7. So this is not possible without elevating the program or playing with NTFS rights.
So I need the configuration file to be written in a common folder like System.Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData).
But this is a fairly common requirement!
So, I'm wondering if there a simple way of achieving this without reinventing the wheel, or if I have to write my own Setting Manager.
After reading the answers here and playing with the ConfigurationManager.Open methods I ended up with the following:
string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "MyApp", "MyApp.config");
Configuration MyAppConfig = ConfigurationManager.OpenMappedExeConfiguration(new ExeConfigurationFileMap { ExeConfigFilename = path }, ConfigurationUserLevel.None);
The nice part is the file doesn't have to exist, and if you call Save() on it, it will create the folder and the file for you. AppSettings["key"] didn't work though so I had to use
MyAppConfig.AppSettings.Settings["key"].Value
to read and write an existing value and
MyAppConfig.AppSettings.Settings.Add("key", value)
to add a new value.
I have had a similar problem and ended up writing my own settings class. It was very basic. I created a Settings class with the properties I needed, and a SettingsManager with Save() and Load() methods that simply serialized/deserialized the object via XmlSerializer into/from a file.
Yes, it is your own code, but it is very simple code, takes less time than trying to figure out whether there is a component providing what you need and how to customize it.
The Application Settings infrastructure does not support this - only non-editable application data and user-scoped data are supported. You can easily read and write your own XML into the CommonApplicationData folders, however, instead of using the application data.
I need to store a few settings (connection strings, folder paths) in a config file.
Should I use App.config?
Seems obvious, but....
1) App. contains some .NET config stuff (packages, versiosn etc) which I don't ever want anyone to be able to touch - I'd rather always these get compiled into the program.
2) It feels weird to have my dev-mode config settings compiled into the program, and invoked when App.Config is missing (defaults to built in resource or something)
3) I like clean config files so I can tell at a glance what the settings are (and I have OCD) ?
Yes, stuff it all into app.config. My reasoning is, nothing in app.config is really editable by the end user, even those bits that only the end user would know (connection strings and folder paths). Do you want a non computer-literate person editing an XML file with Notepad? I know I don't.
The only reliable way to get end-user values into app.config is to prompt the user for them during installation, and write them yourself as a custom action.
Yes, it's best practice, and prevents people from having to recompile your application to make basic config changes: ex. a new user needs to get an automatic email, much easier to alter a config file than recompile and re-deploy the app. In regards to your config file problem, here's an elegant solution: http://www.hanselman.com/blog/ManagingMultipleConfigurationFileEnvironmentsWithPreBuildEvents.aspx
I prefer to just rename the files, or keep commented strings around depending on how much I have to do to move from dev to production.
My rule for *.config files is to use them when I want the ability to change a setting without re-deploying binaries. If I don't care about requiring a deployment to make a change, then I'll use constants. If I'm in doubt, I'll use the config file. I almost always use config files.
When I do use the *.config for something, I'll expose those values through another "Configuration" class that has one static read-only property for each value I wish to expose. i.e. if my app config has a setting
<add key="ServerLoadTime" value="-30" />
Then my configuration class might look like:
public static class Configuration
{
/// <summary>
/// Get the number of minutes before prior to the event that the server is started.
/// </summary>
public static int ServerLoadTime
{
get
{
if (ConfigurationManager.AppSettings["ServerLoadTime"] != null)
return int.Parse(ConfigurationManager.AppSettings["ServerLoadTime"]);
Logging.Write("ServerLoadTime is missing from the Configuration file.", EventLogEntryType.Warning, Logging.Sources.General, "Configuration.ServerLoadTime", null);
return -30; // return a default value.
}
}
}
This approach creates a standardized encapsulation that provides:
A quick way to reference the values without having to go though CM and checking validity.
A surface to deal with badly configured values/missing values.
A place to cast the strings that come from a config file into the type needed.
A place to log missing values while still providing a default-of-last-resort.
A place to deal to attach logic to a result if needed.
I need to have a common application settings file and i need it to be editable by the application at runtime.
I don't want to use the build in settings operation by putting "User" in settings scope because it gets saved in users local directory.
On the other hand i cant use the .config with "Application" as i cannot save into this file from within the application.
One might say "Create your own save settings XML file its easy"..Yes probably it is but i wonder what is the solution for a common writable config file with MS build in classes?
To have data, whatever the format, updateable by all users then all users need to be able to save to whatever container holds that data.
This applies whatever the format, from text file to RDBMS.
The problem with a file that you manage (eg. .config (XML) file) is twofold:
Concurrency.
Access Control.
The ConfigurationManager and associated classes will write to a .exe.config file with applicable declaration of the configuration elements and properties, but the underlying file has to be writeable. You'll need to:
Set an ACL in an installer so all (applicable) users have write access. This means outside the application they can write as well, this is unavoidable as there is no ability to set access control based on application.
Install outside Program Files. UAC virtualises writes to Program Files (so applications assuming all users can write anywhere don't break), but this also means it cannot be used for shared modifiable data.
Concurrency is another problem. What is two users both modify the shared data together? One user will overwrite the others changes unless you implement some locking and reloading on changed.
Consider using a multi-user database for the data instead. They are designed for this kind of thing, Config files are not.
Update (based on comment):
To have a (non-admin) user update a .exe.config in Program Files will still hit UAC virtualisation (ie. any writes will be directed to a per-user location, and not-visible to anyone else).
Easier to use the ConfigurationManager.OpenExeConfiguration(string) method to load a .config from some shared location (eg. in C:\ProgramData1) which has an appropriate ACL. The returned Configuration object has all the capabilities of the implicit load from .exe.config for <appSettings> as well as custom sections, plus access to Save and SaveAs.
1 Of course using correct methods to get actual local path, not hardcoding.
Alright, so I know about the Settings.Properties.Default.* Stuff, but thats not what im trying to use.. I'm trying to develop my own class, ill show you:
public sealed class SettingsHolder<T>
{
public SettingsHolder(){}
public T Setting
{
get{ /*return setting*/ }
set( /*store setting*/ }
}
}
so then i could compile that to a dll, and reference it in an application, and do something like this
SettingsHolder<string> stringHolder = new SettingsHolder<string>();
stringHolder.Setting = "hello world";
and on next application launch
string welcome = stringHolder.Setting;
and welcome should = "hello world".. is this at all possible? i had origannally tried to disect the actual properties.settings class, but with no luck
The .NET/VS settings framework already does exactly what you indicate that you're trying to do, without the clumsy runtime casts and generic classes. Absent a very compelling reason not to use it, that is what you should use. It already handles persisted user-level settings.
If for some reason you absolutely cannot use that, either store the settings directly in a file somewhere in the user's AppData, or use Isolated Storage. You will have to write your own persistence model for it.
If an application could rewrite its own executable code on disk, it would raise all sorts of thorny questions about security and reliability. Besides, security restrictions on Vista and Windows 7 would prevent it from functioning correctly anyway - unless it's a ClickOnce app, it's probably installed in a program folder (i.e. Program Files), and you'd need a user with administrative privileges to elevate the process in order to make it work.
I don't think that's possible. The setting data has to be stored somewhere, in some file or database (when you compile your app you have .exe.config file).
Whe you do new classname() you create the object with members of that class initialised in memory to their default values. e.g. if your SettingsHolder class contained an int _n; it would be initialised to 0. Alternatively, you can of course initialise your member variable to a specific value, e.g. int _n = 5;
As far as I recall, this is in the definition of the language, which therefore precludes what you suggest; which is essentially something along the lines of 'self-modifying code with state'.
Therefore, you do need to use mechanisms to store state in your programs - such as Properties.Settings (which saves to XML config files by default), or a DB for example.
You don't really need to dissect the Properties.Settings class. If you access Settings from the project properties menu, and add a 'User' or 'Application' setting. It can then be accessed with Properties.Settings.Default.SettingName. You do need to instruct the code to save changes to the properties, if I recall correctly.