I have a program A, it also have an app.config file where I have added some keys like server address, username and password for connecting to a server. It is a console application. Now I want to make a UI which I have done. In that UI I want to modify the content of app.config of program A. How do I do that?
Here is what I tried, I copied the UI (basically an .exe) to program A's directory, where the app.config also resides. Then in the UI, I use the ConfigurationManager class's OpenExeConfiguration method, and passing the program A's filename as an argument. But it throws and exception of type System.Configuration.ConfigurationErrorsException.
So I think that my approach is incorrect. How shall I do this?
EDIT: Oh I forgot to tell I'm using C#, .NET 3.5 and VS 2008 (if that helps :D)
I'm not sure about the problem with your approach (try adding the stack trace to your post) but this is how I do it:
var configMap =
new ExeConfigurationFileMap
{
ExeConfigFilename = externalConfigurationFile
};
System.Configuration.Configuration externalConfiguration =
ConfigurationManager.OpenMappedExeConfiguration(
configMap,
ConfigurationUserLevel.None);
foreach (var setting in externalConfiguration.AppSettings.Settings)
{
...
}
externalConfiguration.Save(ConfigurationSaveMode.Full);
Related
I have a desktop WPF application which uses Entity Framework 4.1 Code First approach to manage data.
EF adds a connection string to the App.config file and I wan't to be able to change this connection string at runtime.
Scenario is like this:
When I deploy the application it has a default connection string in the App.config file. The user runs the application and since the connection string will probably be invalid for the user (because of server name, user id and password) I will display a server configuration window.
Here user will enter the valid information about his connection and press OK. I will then be able to change the App.config file and save the user's new valid information to the file.
Problems:
If I change it using ConfigurationManager, the changes will be temporary meaning that the file is not saved, changes are made in memory.
If I read the App.config file into a stream, make required changes in the memory, delete physical file and save the in memory stream as App.config again, Windows will not let me make changes to files under ProgramFiles folder.
What is would be the best approach here?
EDIT: Problem Again!
After I modify the App.config file with this method:
private void ApplyChangesToConnectionString()
{
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var connectionStringsSection = (ConnectionStringsSection)config.GetSection("connectionStrings");
connectionStringsSection.ConnectionStrings["SomeUniqueName"].ConnectionString = GetChangesAppliedConnectionString(connectionStringsSection.ConnectionStrings["SomeUniqueName"].ConnectionString);
config.Save(); // This line throws an exception
ConfigurationManager.RefreshSection("connectionStrings");
}
config.Save(); method call throws an error saying
"Access to "C:\Program Files (x86)\MyCompany\MyApp\MyApp.exe.config"
is denied."
I know that files under "Program files" are immutable, so how can I handle this?
I couldn't modify ConfigurationManager.ConnectionStrings["key"] object, because it is readonly.
So I decided to add a new connection string to my App.config file so it looks like this:
<connectionStrings>
<add name="SomeUniqueName" connectionString="Data Source=(local)\SQLExpress;Initial Catalog=MyDb;User Id=sa;Password=password; MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />
and then changed my DbContext constructor to take newly added connection string like this:
public MyContext()
: base("name=SomeUniqueName")
{
}
Here, the value of name attribute at connection string and constructor must match.
Then to change this newly added connection string at runtime I used a method like this:
private void ApplyChangesToConnectionString()
{
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var connectionStringsSection = (ConnectionStringsSection)config.GetSection("connectionStrings");
connectionStringsSection.ConnectionStrings["SomeUniqueName"].ConnectionString = GetChangesAppliedConnectionString(connectionStringsSection.ConnectionStrings["SomeUniqueName"].ConnectionString);
config.Save();
ConfigurationManager.RefreshSection("connectionStrings");
}
App.config is not the proper place to do this since it's a global configuration used by the application.
I recommend you to save the settings per user. See this related question : c# - approach for saving user settings in a WPF application?
App.config is the correct approach in my opinion, however I wouldn't rely on writing the file physically yourself. Instead, allow the framework to do the heavy lifting for you.
Check out the sample here.
EDIT: Glad Sandeep's comment above has helped you. Feel free to check out that link too if you want a bit more information!
I'm testing my application on a non-administrator windows 7 account. The application is installed into program files. This includes the .sdf file I need to read from. I've got the connection string marked as read only and set the temp path to my documents. This is the error that it spits out when I try to do connection.Open()
Internal error: Cannot open the shared
memory region
I've got the connection string defined in app.config, but I'm modifying it before I start using the connection. This part is in app.config Data Source=|DataDirectory|\DB.sdf;Password=password;
And then I modify it like so:
connection = new SqlCeConnection(connectionString +
";Mode=Read Only; Temp Path=" + Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments));
This works on my developer machine (obviously) since its running from outside of a read-only directory. But even when I manually mark the .sdf file as read-only it still works, and successfully creates the temporary db file in the correct folder. However, on the test machine everything is located in a read-only program files folder, and it doesn't work.
The main goal of this problem is trying to make sure my program doesn't have to be ran as an administrator, and I would like to keep from moving the main copy of the db file from outside of the installation directory.
Let me know if I need to explain anything else. Thanks
I'm using a sql ce database too and had the same problems. my solution was to create the database in a subfolder in Environment.SpecialFolder.CommonApplicationData. If only one user will use it you can create it in Environment.SpecialFolder.ApplicationData. But here you don't need admin rights.
Another point is your connection string in your app.config. If you'll modify it in your program like me, it must be located in such a 'non-admin-right-needed' folder too. I have a static app.config in my app-folder in program files, but a second one with the connection string in Environment.SpecialFolder.LocalApplicationData (this is 'username\AppData\Local' in Win7). And I protect my connectionstring with DataProtectionConfigurationProvider encryption, so no one can read the data base password.
This is how you can map your second app.config to your app:
string ConfigPathString = #"{0}\MyApp\MyApp.config";
string ConfigPath = String.Format( ConfigPathString, System.Environment.GetFolderPath( Environment.SpecialFolder.LocalApplicationData ) );
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
fileMap.ExeConfigFilename = ConfigPath;
Configuration Config = ConfigurationManager.OpenMappedExeConfiguration( fileMap, ConfigurationUserLevel.None );
string myConnectionString = ConnectionStrings.ConnectionStrings["MyConnectionStringKey"].ConnectionString;
Like Calgary already mentioned in his comments you can't really open the file directly in the programs folder due to the restrictions of Windows 7 to non-admins. But due to the fact that you don't want to write anything into it, why don't you simply copy at startup the file into Environment.SpecialFolder.ApplicationData?
When your program starts up simply copy the file out of the programs folder into a proper location, use it as you like and delete it on application exit. So you don't leave any fragments (except the application would crash).
Just to be sure for the last scenario, you could add an additional delete operation to the setup deinstallation routine. So if the application will be removed and it crashed at the last start the setup will remove the trash, leaving the machine as before the installation of the software.
I have the following code in an Winform Application
String[] lines = { "LAST MESSAGE", "101" };
File.WriteAllLines("MPP_Config.txt", lines);
On my Development system, the file gets created under Bin\Debug...
Where will the file be created in the client once it is installed in the client system ?
I deploy to a website using Click once Deployment...
I believe it will be created in current working directory of the application. The application might not have access to this directory, especially on systems with UAC, Vista and windows 7. You should probably think about using the application data directory instead.
String[] lines = { "LAST MESSAGE", "101" };
String fileName = Path.combine(System.Deployment.Application.ApplicationDeployment.CurrentDeployment.DataDirectory,"MPP_Config.txt");
File.WriteAllLines(fileName, lines);
I'm guessing it gets created in the debug folder becuse you have been running it in debug mode. If you ran it in release mode then it will be saved in the bin/release folder.
In other words it will be created in the directory in which the application resides. Why not try it? I.e. copy your exe across...
In the current directory. This means that it might be hard to predict with 100% certainty. If you want to control it, you can use Environment.CurrentDirectory but (as Sam points out in the comments) this may not be a good idea since other code may also depend on the current directory for other purposes.
For instance, the following program will create two different files called "somefile.txt" (given that the exe is not run from the c:\temp directory):
static void Main(string[] args)
{
File.WriteAllText("somefile.txt", "some text");
Environment.CurrentDirectory = #"c:\temp";
File.WriteAllText("somefile.txt", "some text");
}
I have an application that encrypts a section in the configuration file. In the first time that I try to read the encrypted section from the config file I get an error message: "Unrecognized attribute 'configProtectionProvider'. Note that attribute names are case-sensitive. "
config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
// Get the section in the file.
ConfigurationSection section = config.GetSection("EncryptedSection");
if (section != null)
{
// Protect the section.
section.SectionInformation.ProtectSection("RsaProtectedConfigurationProvider");
section.SectionInformation.ForceSave = true;
// Save the change.
config.Save(ConfigurationSaveMode.Modified);
}
ConfigurationManager.RefreshSection("EncryptedSection");
Properties.Settings.Default.Reset();
//This is the part where I read the encrypted section:
ConfigurationManager.RefreshSection("EncryptedSection");
System.Collections.IDictionary HSMMasterKeyConfig = (System.Collections.IDictionary)System.Configuration.ConfigurationManager.GetSection("EncryptedSection");
This only happens in the first time that I try to read the encrypted section. I have noticed that the .config file is getting updated immediately after the first save but from some reason I need to restart the application in order to use the encrypted section.
Have you read through this...
http://bytes.com/groups/net/521818-configurationerrorexception-when-reading-protected-config-section
... as it appears to be a conversation involving an MSFT support engineer that directly maps to your situation.
The best way to do this will be to encrypt the app.config sections during installation only. Add an installer class to your project and override the Install method in the class. In this method you should perform the Encryption. You must call base.Install at the end of your overridden Install method. In the Setup Project goto Custom Actions and locate the Install custom action to be pointed with Your Project output [exe or assembly] which contains the definition of your Installer class implementation. This way it will Encrypt your app.Config sections during an installation straight and you will not face this problem. The application will automatically use DPAPI provider to read/write through sections or settings.
For your reference the issue was that the process that was trying to encrypt the config section didn't have admin rights. I added this process to the administrators group and that solved it.
I just ran into the same problem today. Normally, whenever I start an application where the config is in encrypted I always check the config on startup to determine if it's protected. If not the it I follow the standard SectionInformation.ProtectSection method. This is always my first step but today for some reason I decided to reference something from the config before I performed my protection check and got the "Unrecognized attribute 'configProtectionProvider'. Note that attribute names are case-sensitive. " error. All you have to do is run protection code before you reference the config within your normal code and you will no longer have the error.
Try running your Exe in seperate Application Domain. Once your application is loaded in the new AppDomain, check if the Sections are encrypted or not. If not then Encrypt the section and trigger the AppDomain to unload and reload with your executable again.
I have some settings in my app.config which I intend to be 'global' - ie. any user can change them, and all users get the same setting.
But unless I change them to be user settings, they are read only.
Why is this?
And how should I go about persisting my app's global settings?
Edit:
This is actually a windows service application which runs as a service as LocalSystem. It can also be run manually by a local admin with argument "/config", which launches a windows form to edit configuration values.
So it will have write access to %PROGRAMFILES% in both situations.
The way I am accessing my settings is thusly:
Settings.Default.MySetting = MyNewValue;
And when MySetting is set to Application (in my project properties, Settings.settings), I get a compile-time error "MySetting is read only".
I am new to this stuff, and have not yet found a very good explanation of how it is supposed to be done. For example, why do I need to say 'Default', and what does that actually mean? I have no idea. If anyone can point me to an app.config usage tutorial, that would be really helpful.
The real complete answer:
The app.config settings are read-only because there are 2 types of settings:
Application Settings
User Settings
The first won't change unless the application publisher publishes a new version of it. The second is not stored in the app.config, but in a user.config file. In the abscence of this user.config file the app.config provides the default value.
If MySetting is a User Setting:
Settings.Default.MySetting = MyNewValue;
Settings.Default.Save();
It will create a user.config file at [User Local Settings Application Data]\[company name]\[application].exe[hash string]\[version] with the new settings, and those settings will prevail over the settings in the app.config file.
Why: Application settings are intended to be stored in the Application folder under Program Files where the user does not have write privileges.
How: There is no default support for "All Users" but you should be able to setup your own custom config file in a public folder or use a Database.
Simply put: There's no location on a machine that everyone can change, unless you give privileges to do so.
There are several ways to deal with this kind of situation:
You can create a configuration file / some registry settings, put this in the "all users" profile and grant "Everyone" the rights to change that specific file. During installation you can automate the procedure for granting the appropiate privileges and your program can handle the rest.
You can leverage UAC to make sure the current user has the appropiate privileges to change a system-wide setting. This is the recommended approach but also means that not everyone can change specific settings.
You can use a shared database and store your settings in there.
???
I would not recommend to change items in the program files directory or changing the default privileges overthere.
EDIT: As local system you have indeed write privileges to the program files directory. If you get the "Read only" error, it means the settings itself are read only. You'll need to use the configuration manager to be able to change the settings in configuration files.
Hope this helps.
One reason is that the app.config file is in your app's folder under the Program Files directory, and everything in Program Files is read only for standard users by default.
Another is that app.config settings apply system wide. If one user makes a change it will impact other users. Normal users are not supposed to be able to make that kind of change. Anything that can impact multiple users should only be set by a system administrator. Per-user settings belong in each user's Application Data folder.
Not quite sure what you mean here.
Do you mean you allowed users to alter app.config from the UI and the changes are not persisted?
did you call
ConfigurationManager.RefreshSection("appSettings");
and
Configuration.Save();
Configuration Settings are cached in the memory when you starts the application. you can deal with the app.config file as xml to change the values.
I'm using this code (a static method) to change the default settings:
public static bool SetGlobalSetting(string settingName, string settingValue)
{
try
{
XmlDocument xml = new XmlDocument();
xml.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
XmlNode xmlNode = xml.DocumentElement.SelectSingleNode("descendant::setting[#name='" + settingName + "']");
xmlNode.SelectSingleNode("value").InnerText = settingValue;
xml.Save(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
Settings s = new Settings();
s.Reload();
return true;
} catch (Exception e)
{
// process the exception as you need
}
return false;
}
}