Changing values of settings files runtime - c#

I'm working on a windows based project , in this project we used multiple settings files in order to set text of controls for example buttons.settings, lables.settings and .....
now I need to change the content of these settings files with other values at run time, for this reason we created same settings files with same column "Name" but different values, now I really have problem with changing content of these settings files.
I tried to change content of my tow settings file by loading and saving them as xmlDocument, but unfortunately my app.config doesnt change by new values.
I also used ConfigurationManager.RefreshSection ...
plz help me
thnx in advance

I'll start from describing my setup, just to be sure that we are on the same page.
Thats my setting file - Settings1.settings, with just one setting Test, & the default value being DefaultValue
At this point, the default value is also copied to app.config.
Now, I have a template whose settings which shall come into effect at run time, Its in the form of user.config. And this is how it looks like -
here is the code from working experiment -
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show(Settings1.Default.Test); // this shows "DefaultValue" in a message box
// Now change the user.config file with our template file -
//1. I get the location of user config
var fileForUser = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal).FilePath;
//2. now I'll Place my template file, where user.config should be present
// create directory if it doesnt exist
if(Directory.Exists(Path.GetDirectoryName(fileForUser)) == false)
Directory.CreateDirectory(Path.GetDirectoryName(fileForUser)) ;
// I have kept my template at E:\template.config
File.Copy(#"E:\template.config", fileForUser, true);
MessageBox.Show(Settings1.Default.Test); // this still shows "DefaultValue" because the user.config is not reloaded
//3. Read the new setting
Settings1.Default.Reload();
MessageBox.Show(Settings1.Default.Test); // this shows "Default Value is changed to ABC" because the user.config is now reloaded
}
The App.config remains as it is & incase I delete the user.config or call Settings1.Default.Reset() then its the App.config which provides the application with default values
Hope it helps. Do let me know if it served yr purpose or not.
Update 1 Supporting the already tried approach by author of the question
Here is the working code to support yr approach, which will bring the settings file's setting in applicaion -
regret my typo - Lables2.settings, Lables.settings instead of Labels2.settings & Labels.settings
{
// 1. Open the settings xml file present in the same location
string settingName = "Lables2.SETTINGS"; // Setting file name
XmlDocument docSetting = new XmlDocument();
docSetting.Load(Application.StartupPath + Path.DirectorySeparatorChar + settingName);
XmlNodeList labelSettings = docSetting.GetElementsByTagName("Settings")[0].ChildNodes;
// 2. Open the config file
string configFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
XmlDocument appSettingDoc = new XmlDocument();
appSettingDoc.Load(configFile);
XmlNodeList appConfigLabelSettings = appSettingDoc.GetElementsByTagName("userSettings")[0].
SelectNodes("WindowsFormsApplication2.Lables")[0].ChildNodes;
//ProjectName.Setting file
//3. update the config file
for (int i = 0; i < appConfigLabelSettings.Count; i++)
{
var v = appConfigLabelSettings.Item(i).ChildNodes[0];
v.InnerText = labelSettings.Item(i).InnerText;
}
//4. save & load the settings
appSettingDoc.Save(configFile);
Lables.Default.Reload();
MessageBox.Show(Lables.Default.Code); // test pass... shows A2
}
My project settings -
Thats the executable folder, where
And this is how the labels2.settings look like
Update 2 Approach without xml document
All the setup is same & this is much cleaner. Please try -
{
// 1. Open the settings xml file present in the same location
string settingName = "Lables2.SETTINGS"; // Setting file name
XmlDocument docSetting = new XmlDocument();
docSetting.Load(Application.StartupPath + Path.DirectorySeparatorChar + settingName);
XmlNodeList labelSettings = docSetting.GetElementsByTagName("Settings")[0].ChildNodes;
Console.WriteLine("Code {0} Group{1} Name{2}", Lables.Default.Code, Lables.Default.Group, Lables.Default.Name); //prints Code A1 GroupB1 NameC1
//2. look for all Lables2 settings in Label settings & update
foreach (XmlNode item in labelSettings)
{
var nameItem = item.Attributes["Name"];
Lables.Default.PropertyValues[nameItem.Value].PropertyValue = item.InnerText;
}
Lables.Default.Save(); // save. this will save it to user.config not app.config but the setting will come in effect in application
Lables.Default.Reload();
Console.WriteLine("Code {0} Group{1} Name{2}", Lables.Default.Code, Lables.Default.Group, Lables.Default.Name); //prints Code A2 GroupB2 NameC2
}

Perhaps its the problem with xmlDocument as mentioned here Changing App.config at Runtime
Please keep the setup same as my last response of label.settings & label2.settings.
And try this implementation
{
// 1. Open the settings xml file present in the same location
string settingName = "Lables2.SETTINGS"; // Setting file name
XmlDocument docSetting = new XmlDocument();
docSetting.Load(Application.StartupPath + Path.DirectorySeparatorChar + settingName);
XmlNodeList labelSettings = docSetting.GetElementsByTagName("Settings")[0].ChildNodes;
Console.WriteLine("Code {0} Group{1} Name{2}", Lables.Default.Code, Lables.Default.Group, Lables.Default.Name); //prints Code A1 GroupB1 NameC1
//2. look for all Lables2 settings in Label settings & update
foreach (XmlNode item in labelSettings)
{
var nameItem = item.Attributes["Name"];
Lables.Default.PropertyValues[nameItem.Value].PropertyValue = item.InnerText;
}
Lables.Default.Save(); // save. this will save it to user.config not app.config but the setting will come in effect in application
Lables.Default.Reload();
Console.WriteLine("Code {0} Group{1} Name{2}", Lables.Default.Code, Lables.Default.Group, Lables.Default.Name); //prints Code A2 GroupB2 NameC2
}
It works for me, & because its without xmldocument, I'm hopeful it'll work at yr end too. Do let me know the result.

XmlDocument doc = new XmlDocument();
//doc.Load(#"C:\Users\***\Documents\Visual Studio 2008\Projects\ChangingLablesRuntime\ChangingLablesRuntime\_Labels2.settings");
//doc.Save(#"C:\Users\SHYAZDI.IDEALSYSTEM\Documents\Visual Studio 2008\Projects\ChangingLablesRuntime\ChangingLablesRuntime\_Labels.settings");
doc.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
var root = doc.GetElementsByTagName("userSettings")[0];
doc.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
var Config = System.Configuration.ConfigurationManager.OpenExeConfiguration(#"path of app.config");
var root = doc.GetElementsByTagName("userSettings")[0];
doc.GetElementsByTagName("userSettings")[0].SelectSingleNode("Zeus._Labels").InnerText = doc.GetElementsByTagName("userSettings")[0].SelectSingleNode("ChangingLablesRuntime._Labels2").InnerText;
//var newEml = root.SelectSingleNode("ChangingLablesRuntime._Labels2");
//var oldEml = root.SelectSingleNode("Zeus._Labels");
//oldEml.InnerText = newEml.InnerText;
//oldEml.ParentNode.ReplaceChild(newEml, oldEml);
doc.Save(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
Config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("userSettings");
here is my code , lables2 is same as lables1 with different values, after running this code nothing happened.
this is piece of lables1.settings , that I want to replace with lables2.settings:
this is piece of lables2.settings :
and app.config related code :

Related

Refresh IIS site custom settings without restarting it

I have a custom settings file (Settings.xml) where I specify all that I need. When changeing that file while running the website the site, of course, restart.
Settings.xml is read into an object that have the same structure as the xml and then I work with the Settings object
private static readonly XDocument Settings = XDocument.Load(AppDomain.CurrentDomain.BaseDirectory + "\\Settings.xml");
The site runs in IIS 8.5.
Is it possible to update Settings.xml without forceing the website to restart?
Is it the IIS that automatically restart?
You can read your setting on every file change like this:
private static DateTime? _lastSettingRead;
private static XDocument _cahedSettings;
private static XDocument Settings
{
get
{
var settingsPath = AppDomain.CurrentDomain.BaseDirectory + "\\Settings.xml";
//Get last change Settings file datetime
var lastSettingChange = System.IO.File.GetLastWriteTime(settingsPath);
//If we read first time or file changed since last read
if (!_lastSettingRead.HasValue || lastSettingChange > _lastSettingRead)
{
_lastSettingRead = lastSettingChange; //change read date
_cahedSettings = XDocument.Load(settingsPath); //load settings to field
}
return _cahedSettings; //return cached settings from field
}
}

receiving unauthorized exception when changing XML file

I have an application that changes your connectionstring in a webconfig in your unauthorized exception. Is there a way I can allow the file to be accessed? Thanks for any help.
Exception Message: System.UnauthorizedAccessExeption: Access to the path "PATH" is denied.
//File path to xml file
var adminConnectionStringConfig = new FileInfo(e.Data.Details.VSStudio.Solution.FullName).Directory
+ #"\Web.Admin\ConnectionStrings.config";
//Method that updates the xml file
private void SetupConnectionStrings(string path, string newDb)
{
var doc = new XmlDocument();
doc.Load(path);
XmlNode node = doc.SelectSingleNode("configuration/connectionStrings");
for (int i = 0; i < node.ChildNodes.Count; i++)
{
if (node.ChildNodes[i].Attributes["name"].Value == "SYS")
{
//Get ConnectionString for client project
var connectionString = node.ChildNodes[i].Attributes["connectionString"].Value;
// Cut all
var dbName = node.ChildNodes[i].Attributes["connectionString"]
.Value.Substring(connectionString.IndexOf("Catalog="));
dbName = dbName.Replace("Catalog=", "");
//Db Name that we will now replace
dbName = dbName.Substring(0, dbName.IndexOf(";"));
// System.Diagnostics.Debugger.Launch();
doc.InnerXml = doc.InnerXml.Replace(dbName, newDb);
doc.Save(path);
break;
}
}
}
There are some steps that you can take.
First, I'd make sure that your XML file is not read-only.
If that doesn't work, than you can run the project as an admin. there are some directories that windows doesn't let you modify if you're not an admin.
If you right click on visual studio, and select run as administrator, then It will automatically debug your project as an admin.

How to launch a process which will open a text file in any editor and automatically move cursor to a certain line number?

From c#, I want to launch a process which will open a text file in any editor and automatically move cursor to a certain line number.
I can open a file using
Process.Start(#"c:\myfile.txt");
but I don't know how to move cursor at specific location in that file.
Answer with source code:
yes, I used notepad++
private void openLog() {
try {
// see if notepad++ is installed on user's machine
var nppDir = (string)Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Notepad++", null, null);
if (nppDir != null) {
var nppExePath = Path.Combine(nppDir, "Notepad++.exe");
var nppReadmePath = Path.Combine(yourDirectory,fileName );
var line = 20;
var sb = new StringBuilder();
sb.AppendFormat("\"{0}\" -n{1}", nppReadmePath, lineNo);
Process.Start(nppExePath, sb.ToString());
} else {
string newPath = #"\\mySharedDrive\notpad++\bin\notepad++.exe";
Process.Start(newPath, #"\\" + filePath + " -n" + lineNo); // take exe from my shared drive
}
} catch (Exception e) {
Process.Start(#"\\" + FilePath); // open using notepad
}
}
Get Notepad++, then you can do this:
var nppDir = (string)Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Notepad++", null, null);
var nppExePath = Path.Combine(nppDir, "Notepad++.exe");
var nppReadmePath = Path.Combine(nppDir, "readme.txt");
var line = 20;
var sb = new StringBuilder();
sb.AppendFormat("\"{0}\" -n{1}", nppReadmePath, line);
Process.Start(nppExePath, sb.ToString());
In this example we get install path of n++ from the registry, build path to exe and readme.txt file, opens its own readme.txt file with cursor on line 20.
Using StringBuilder is more efficient than using multiple appends (explanation somewhere on SO).
The solution very heavily depends on which process/editor is opened on your system. That editor would have to have a developer API that you could use to access functionality such as setting ranges and altering the cursor position. For example, if the editor that is opened is Microsoft Word, you would use the Word Interop API to set a selection at a specific position. There is no universal way to do this in 'any editor' since each one has its own API (or no outward facing API at all).
Perhaps you are going this the wrong way. I'm not sure what you are trying to accomplish, but I think it would be alot easier to just open the text file in an editor that belongs to your application. Perhaps another form with a WYSIWYG editor control. That way you have full control on where the cursor will land in that editor. Otherwise, there are just way too many unknowns for anything feasibly workable.

C# loop through app.config but only use keys like "service"

I'm looping through a txt file currently to get server names and check a specific services status and start it if stopped. This works perfectly with my service names stored in my app.config.
What I want to do is also store my file path, file name, timeout, and any other keys I want to put in the app.config down the road.
My issue is when I loop through the app.config currently I only have service names which works perfectly. If I add the other keys that I want to add I obviously will get "Service Not Found".
How do I only pick keys that are "like" "service". I'm naming the keys "service1", 2, 3, etc.
foreach (string key in ConfigurationManager.AppSettings)
{
string value = ConfigurationManager.AppSettings[key];
// Create an instance of StreamReader to read from a file.
// The using statement also closes the StreamReader.
using (StreamReader sr = new StreamReader(txtFilePath + txtFile))
{
String line;
// Read and display lines from the file until the end
// of the file is reached.
while ((line = sr.ReadLine()) != null)
{
Console.WriteLine(line);
// Check for lines with semi-colon. If semi-colon at
// line start skip it
if (line.StartsWith(";"))
{
continue;
}
else
{
ServiceController sc = new ServiceController(value, line);
//Create new timeout module.
TimeSpan timeout = new TimeSpan();
//Write current service status to console.
Console.WriteLine(
"The " + value + " service status is currently set to {0}",
sc.Status.ToString()
);
You should move to a named configuration section with elements that you define VS using the AppSetting collection.
I'd follow the recommendations here on how to make a custom section in your config file, and then you're no longer finding items by key, but by section.

How do I get build details in a custom workflow activity?

I need to add a custom activity to the default workflow template to increase assembly versions at the earliest point possible in the build process.
What I would like to achieve is to create and map the exact same workspace (that is be created further down in the workflow) inside my custom activity so that I can check out an xml file, increase the version number held within, write it back to the xml file and check the xml file back in.
I'm aware that this workspace will be created later on in the workflow but that will be too late in the build process for what I'm trying to achieve, so instead of moving any of the activities or duplicating them in a position above my custom activity (this should be ok as this workspace will be deleted and recreated again later)
I think the details I need are the BuildDirectory, WorkspaceName and SourcesDirectory. Can anyone tell me how to achieve the creation of the workspace or how obtain this data in code?
the build will be carried out on a build server, and I am using TFS 2010 and c#.
Thanks in advance
I followed the series of blog articles by Ewald Hofman as a primer and created a custom activity that does the check-out, update and check-in of a GlobalAssemblyInfo file that I parse the current version from. My task is inserted at the top of the "Update Drop Location" which is right after it does the "Get the build" portion of the workflow. I just use require the IBuildDetail and a File Mask as arguments from which you can pull out the VersionControlServer to be able to access TFS. My code is below:
protected override string Execute(CodeActivityContext context)
{
// Obtain the runtime value of the input arguments.
string assemblyInfoFileMask = context.GetValue(AssemblyInfoFileMask);
IBuildDetail buildDetail = context.GetValue(BuildDetail);
var workspace = buildDetail.BuildDefinition.Workspace;
var versionControl = buildDetail.BuildServer.TeamProjectCollection.GetService<VersionControlServer>();
Regex regex = new Regex(AttributeKey + VersionRegex);
// Iterate of the folder mappings in the workspace and find the AssemblyInfo files
// that match the mask.
foreach (var folder in workspace.Mappings)
{
string path = Path.Combine(folder.ServerItem, assemblyInfoFileMask);
context.TrackBuildMessage(string.Format("Checking for file: {0}", path));
ItemSet itemSet = versionControl.GetItems(path, RecursionType.Full);
foreach (Item item in itemSet.Items)
{
context.TrackBuildMessage(string.Format("Download {0}", item.ServerItem));
string localFile = Path.GetTempFileName();
try
{
// Download the file and try to extract the version.
item.DownloadFile(localFile);
string text = File.ReadAllText(localFile);
Match match = regex.Match(text);
if (match.Success)
{
string versionNumber = match.Value.Substring(AttributeKey.Length + 2, match.Value.Length - AttributeKey.Length - 4);
Version version = new Version(versionNumber);
Version newVersion = new Version(version.Major, version.Minor, version.Build + 1, version.Revision);
context.TrackBuildMessage(string.Format("Version found {0}", newVersion));
return newVersion.ToString();
}
}
finally
{
File.Delete(localFile);
}
}
}
return null;
}

Categories

Resources