How to access app configuration from a .dll? - c#

I recently broke out a part of my winform app in a .dll. Some of the classes in that dll
wants fetch/store user settings.
The classes just used the VS generated Settings file so it just did
Properties.Settings.Default.SomeSetting = var;Properties.Settings.Default.Save() etc.
What are my options now that I moved that code out to a class library/.dll ?

The hosting application should handle the interface to the config file, not the DLL. Either
Pass whatever settings need to be read/modified within the DLL as parameters, or
Pass in a name-value collection of settings that can be modified by the DLL, and save whatever changes are made by the DLL to the collection when control returns to the calling application.
This is similar in principle to removing a database interface from the business layer of a tiered application and encapsulating it into a data layer.

It doesn't make a lot of sense to me to have a DLL storing user settings. A DLL is a library, not an application, and doesn't directly interact with the user. If classes in the DLL need access to user settings, you can pass them in as parameters.

The Properties class is autogenerated. It is really a wrapper on the config file. If you don't want to change your design, just go into the code and copy it to your DLL. But remember it will no longer be magically maintained (regenerated). Or you can use ConfigurationManager to get at config file directly.

I would not recommand it (better use your own class for settings), but you can try this:
string sectionName = "applicationSettings/" +
appName + ".Properties.Settings";
System.Configuration.ClientSettingsSection section =
(System.Configuration.ClientSettingsSection)
System.Configuration.ConfigurationManager.GetSection(sectionName);
foreach (SettingElement setting in section.Settings)
{
string value = setting.Value.ValueXml.InnerText;
string name = setting.Name;
if (name.ToLower().StartsWith(searchName.ToLower()))
{
return value;
}
}

For those who need to read settings from userDirectory/user.config, here is a solution:
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal);
ConfigurationSectionGroup userSettings = config.GetSectionGroup("userSettings");
ClientSettingsSection settings = (ClientSettingsSection)userSettings.Sections.Get("[applicationName].Properties.Settings");
SettingElement elem = settings.Settings.Get([settingName]);
var sett = elem.Value.ValueXml.InnerText;

Related

Adding a user Settings property at runtime (Winforms)

I am using C# ,win forms and .net 2.0
I am trying to add a property inside user settings file at run time but i am not able to view that added property in settings.settings file in certain location i.e. file exists but property is not added
I am not getting error when i call this property it works
Using this below code
MessageBox.Show(***********.Properties.Settings.Default.Properties["NewProperty"].DefaultValue);
I have written this following code
Calling the function
clCommonFuncation cl = new clCommonFuncation();
if (***********.Properties.Settings.Default.Properties["NewProperty"] == null)
{
cl.addPropertyinSettingsFile("NewProperty",
***********.Properties.Settings.Default.Providers,
***********.Properties.Settings.Default.Providers["LocalFileSettingsProvider"],
***********.Properties.Settings.Default.Properties,
typeof(string),"ASD",null);
***********.Properties.Settings.Default.Save();
***********.Properties.Settings.Default.Reload();
}
And this is calling funaction
public void addPropertyinSettingsFile(string settingName,
SettingsProviderCollection settingsProviderCollection,
SettingsProvider settingsProvider,
SettingsPropertyCollection settingPrpertyCollection,
Type dataType,
object defaultValue,
object settingDefault)
{
SettingsProperty lvSettingProperty = new SettingsProperty(settingName);
lvSettingProperty.DefaultValue = defaultValue;
lvSettingProperty.IsReadOnly = false;
lvSettingProperty.PropertyType = dataType;
lvSettingProperty.Provider = settingsProvider;
lvSettingProperty.SerializeAs = SettingsSerializeAs.String;
lvSettingProperty.Name = settingName;
lvSettingProperty.Attributes.Add(typeof(System.Configuration.UserScopedSettingAttribute),
new System.Configuration.UserScopedSettingAttribute());
settingPrpertyCollection.Add(lvSettingProperty);
}
What is it i am doing wrong?
Any suggestion will be appreciated
Thank you
I think you'd better write a custom struct or class with your application settings and use serialization for loading and saving it - that is much more clear and relevant in your case.
You can not add or remove properties in .Net Setting files at runtime. There are some tricks round the web but none of them is applicable and a solution to what you want.
Settings files have not been designed for such a purpose. These files have been designed to be populated at design time and only be "read" or "modified" during runtime.
The reason is that when you create and edit settings file in designer (by double clicking on a settings file in solution explorer or choosing settings tab at project properties menu item) Visual studio creates an ApplicationSettings class (Derived from ApplicationSettingsBase class) which has data members for any setting field you created plus some additional attributes (like [ApplicationScopedSetting] or [UserScopedSetings] ). At run time the .Net runtime interacts with seetings files using this class. Therefore when you try to add properties at run time, you have no backing fileds and attributes in the ApplicationSettings class and CLR does not know what to do with them.
Conclusion: The Settings file have specific usage and are not suitable for any arbitrary configuration persistence in you application. Try using XML files which fully support what you want from reading, writing, adding, removing, updating basic types properties (string, char, int and so on) to supporting complex objects using XML.Seriliazation.

Load a WPF app.config / MyApp.exe.config from a location different from the app binary directory

I would like to load the MyApp.exe.config from a sub folder of the app bin folder rather than the app bin folder itself (eg ./Configs/MyApp.exe.config).
I dont want to have to work with a System.Configuration object (as returned by ConfigurationManager.OpenExeConfiguration(configPath) because this is just a string map. I would like to keep working with the existing generated Settings : ApplicationSettingsBase object in Settings.Designer.cs as that has setting values cast to proper objects.
I.e I just want to redirect where Settings loads itself from. Ive had a look round and all I can find are solutions that involve working directly with System.Configuration object directly - but how to rewire this up to Settings?
Kind of seems a reasonable thing to want to do - can't understand why it appears so difficult? Any solutions most welcome!
Jims solution is clean when you have a single configSection in the app.config. In our case we have several configSections (for app, libraries, nlog etc) and we want to just load the whole file from a new location. This wasnt really clear from the orignal question.
The eaisest way to do this seemed to be to use a new AppDomain with an AppDomainSetup object on which we set the ConfigurationFile property to path to new config file.
The question is then when and how to create the new app domain. This post offers an elegant solution that seems to work with minor modifications.
1: Add a Startup event handler:
Application x:Class="InstallTool.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Window1.xaml"
Startup="AppStartup"
2: In that handler (in the default application domain) create a new application domain - the creation of which just recursively calls the Startup event handler. When the new app domain is finsihed with (app closed) just closed it an abort startup in the "outer" app domain. This avoids having to make cross app domain calls or have a bootstrapper app to create the new app domain.
public void AppStartup(object sender, StartupEventArgs e) {
if (AppDomain.CurrentDomain.IsDefaultAppDomain()) {
string appName = AppDomain.CurrentDomain.FriendlyName;
var currentAssembly = Assembly.GetExecutingAssembly();
// Setup path to application config file in ./Config dir:
AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationBase = System.Environment.CurrentDirectory;
setup.ConfigurationFile = setup.ApplicationBase +
string.Format("\\Config\\{0}.config", appName);
// Create a new app domain using setup with new config file path:
AppDomain newDomain = AppDomain.CreateDomain("NewAppDomain", null, setup);
int ret = newDomain.ExecuteAssemblyByName(currentAssembly.FullName, e.Args);
// Above causes recusive call to this method.
//--------------------------------------------------------------------------//
AppDomain.Unload(newDomain);
Environment.ExitCode = ret;
// We get here when the new app domain we created is shutdown. Shutdown the
// original default app domain (to avoid running app again there):
// We could use Shutdown(0) but we have to remove the main window uri from xaml
// and then set it for new app domain (above execute command) using:
// StartupUri = new Uri("Window1.xaml", UriKind.Relative);
Environment.Exit(0);
return;
}
}
What you need is a custom SettingsProvider. The default for your local application is LocalFileSettingsProvider, which gets the settings from the appname.exe.config file in the same directory as appname.exe. You might be able to create a class derived from LocalFileSettingsProvider, which looks in a different directory. Failing that, you'll have to derive from SettingsProvider.
Also see ApplicationSettingsBase.Providers property.
Ricibob's solution works fine, if you don't have Threads/Background tasks in your application. If you do have them it causes problems. Moreover you can achieve the above solution in one line:
AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", YOUR_CONFIG_LOCATION);

c# forms - Programatically add missing entrys in .config file

a question I haven't found an answer to after googling for a long time (then a long break from it and searching again)...
Say, I've got 2 Settings in my application settings. String1 and String2. Say, further, we shipped the product and start adding minor features (more things to configure) and we add a String3.
How, without traversing the .config file manually, can I add missing entries? When shipped as an update (without OneClick btw.) the existing .config file only has String1 and String2.
While defaulting for String3, the application somehow understands that an entry is missing, so it ought to be possible, or so I think, to add this one setting with the default value, so that either another program or a user, doesn't have to type the whole tags manually, without knowing what name it really is.
Thanks in advance!
Qudeid
Hi folks again,
I've just whipped up the following piece of code that works for me as I wanted.
Just to explain it a little:
I first open the config file using the ConfigurationManager, get the corresponding section and set the ForceSave to true, so that section is sure to save.
Then, the "magic" starts. I iterate through all properties of the assembly's settings and let linq do its magic to find if the element exists. If not, I create it and append it to the file.
Note: This piece of code is only for application settings, not so for user settings as this is a different section. I haven't tried/tested it, but it could be as simple as changing this line:
ConfigurationSectionGroup sectionGroup = configFile.SectionGroups["applicationSettings"];
to this line:
ConfigurationSectionGroup sectionGroup = configFile.SectionGroups["userSettings"];
as this is the corresponding name of that section. No guarantees, though.
Here's my code:
/// <summary>
/// Loads own config file and compares its content to the settings, and adds missing entries with
/// their default value to the file and saves it.
/// </summary>
private void UpdateSettings()
{
// Load .config file
Configuration configFile = ConfigurationManager.OpenExeConfiguration(typeof(Settings).Assembly.Location);
// Get the wanted section
ConfigurationSectionGroup sectionGroup = configFile.SectionGroups["applicationSettings"];
ClientSettingsSection clientSettings = (ClientSettingsSection)sectionGroup.Sections[0];
// Make sure the section really is saved later on
clientSettings.SectionInformation.ForceSave = true;
// Iterate through all properties
foreach (SettingsProperty property in Settings.Default.Properties)
{
// if any element in Settings equals the property's name we know that it exists in the file
bool exists = clientSettings.Settings.Cast<SettingElement>().Any(element => element.Name == property.Name);
// Create the SettingElement with the default value if the element happens to be not there.
if (!exists)
{
var element = new SettingElement(property.Name, property.SerializeAs);
var xElement = new XElement(XName.Get("value"));
XmlDocument doc = new XmlDocument();
XmlElement valueXml = doc.ReadNode(xElement.CreateReader()) as XmlElement;
valueXml.InnerText = property.DefaultValue.ToString();
element.Value.ValueXml = valueXml;
clientSettings.Settings.Add(element);
}
}
// Save config
configFile.Save();
}
When you create a setting in Visual Studio (Project -> Properties -> Settings.settings) you assign a value to that setting in the settings editor. From the settings definition (really an XML file) a code file is generated with a class that gives you access to the settings. This class will as a default use the value assigned to the setting in the settings editor. However, when the setting is accessed it will look for a value of that setting in the App.config file. If there is a value it will override the default value in the code generated file.
What this means is that if you add a setting to your project but doesn't provide a value for that setting in the App.config file the value of the setting will be the default value assigned in the settings editor.
To override the value assign it in the App.config file for the application.
Because your application can be split into multiple assemblies created by multiple projects there is no way to automate a process where adding a setting in a dependent assembly creates an entry for that setting the App.config file for the main project. You have to do that yourself I'm afraid.
But that is exactly the beauty of the system: Two .exe projects can have a dependency on the same .dll project that defines a setting. In each .exe project you can override the setting in the App.config file for the .exe project or you can decide to use the default value defined by the .dll project.

Get executing assembly name from referenced DLL in C#

What is the best way to get the application name (i.e MyApplication.exe) of the executing assembly from a referenced class library in C#?
I need to open the application's app.config to retrieve some appSettings variables for the referenced DLL.
To get the answer to the question title:
// Full-name, e.g. MyApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
string exeAssembly = Assembly.GetEntryAssembly().FullName;
// or just the "assembly name" part (e.g. "MyApplication")
string exeAssemblyName = Assembly.GetEntryAssembly().GetName().Name;
As mentioned by #Ben, since you mention wanting to get the configuration information, use the ConfigurationManager class.
If you want to get the current appdomain's config file, then all you need to do is:
ConfigurationManager.AppSettings....
(this requires a reference to System.Configuration of course).
To answer your question, you can do it as Ray said (or use Assembly.GetExecutingAssembly().FullName) but I think the problem is easier solved using ConfigurationManager.
To get the exact name without versions, etc. use:
string appName = Assembly.GetEntryAssembly().GetName().Name;
Works with .NET v1.1 and later.
You can get the assembly through the class type...
typeof(MyClass).Assembly
If you want the name of the parent EXE and not the referenced DLL assembly - you will need to use this:
Assembly.GetEntryAssembly().GetName().Name
This will return the EXE name (minus the .EXE part).
Using GetExecutingAssembly() is not right as per the OP's question (first paragraph of it!) as it will return the DLL name.
You should never couple your libraries to a consumer (in this case Web, WinForm or WCF app). If your library needs configuration settings, then GIVE it to the library.
Don't code the library to pull that data from a consumer's config file. Provide overloaded constructors for this (that's what they are for).
If you've ever looked at the ConfigurationManager.AppSettings object, it is simply a NameValueCollection. So create a constructor in your library to accept a NameValueCollection and have your consumer GIVE that data to the library.
//Library
public class MyComponent
{
//Constructor
public MyComponent(NameValueCollection settings)
{
//do something with your settings now, like assign to a local collection
}
}
//Consumer
class Program
{
static void Main(string[] args)
{
MyComponent component = new MyComponent(ConfigurationManager.AppSettings);
}
}
If you want to read (and display) version number:
Assembly ass = System.Reflection.Assembly.GetExecutingAssembly();
AssemblyName assname = ass.GetName();
Version ver=assname.Version;
Somewhere in application (ie Title block in a Windows form)
this.Text = "Your title Version " + ver;
For the short name of an assembly from a class instance:
Me.GetType ().Assembly.GetName().Name

Accessing App.config in a location different from the binary

In a .NET Win console application, I would like to access an App.config file in a location different from the console application binary. For example, how can C:\bin\Text.exe get its settings from C:\Test.exe.config?
using System.Configuration;
Configuration config =
ConfigurationManager.OpenExeConfiguration("C:\Test.exe");
You can then access the app settings, connection strings, etc from the config instance. This assumes of course that the config file is properly formatted and your app has read access to the directory. Notice the path is not "C:\Test.exe.config" The method looks for a config file associated with the file you specify. If you specify "C:\Test.exe.config" it will look for "C:\Test.exe.config.config" Kinda lame, but understandable, I guess.
Reference here: http://msdn.microsoft.com/en-us/library/system.configuration.configurationmanager.openexeconfiguration.aspx
It appears that you can use the AppDomain.SetData method to achieve this. The documentation states:
You cannot insert or modify system entries with this method.
Regardless, doing so does appear to work. The documentation for the AppDomain.GetData method lists the system entries available, of interest is the "APP_CONFIG_FILE" entry.
If we set the "APP_CONFIG_FILE" before any application settings are used, we can modify where the app.config is loaded from. For example:
public class Program
{
public static void Main()
{
AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", #"C:\Temp\test.config");
//...
}
}
I found this solution documented in this blog and a more complete answer (to a related question) can be found here.
Use the following (remember to include System.Configuration assembly)
ConfigurationManager.OpenExeConfiguration(exePath)
You can set it by creating a new app domain:
AppDomainSetup domainSetup = new AppDomainSetup();
domainSetup.ConfigurationFile = fileLocation;
AppDomain add = AppDomain.CreateDomain("myNewAppDomain", securityInfo, domainSetup);
AppDomainSetup domainSetup = new AppDomainSetup();
domainSetup.ConfigurationFile = #"D:\Mine\Company\";
string browserName = ConfigurationManager.AppSettings["browser"];

Categories