Visual Studio has the option to apply a Build Action for the App.config file as "Embedded Resource", which means including in the same final exe the content of the App.config.
Fine.
The problem is: how to read the data inside the embedded App.config? For example an appSetting value from a given key?
The code I used before to read from the App.config (the one phisically written on the disk which usually is nameoftheprogram.exe.config), seems to be not working anymore.
string s = System.Configuration.ConfigurationManager.AppSettings["mykey"];
Probably it must be re-adapted with other C# classes designed for this job.
Any ideas?
You can have interface IConfigUtility with method :
IConfigUtility.cs:
public interface IConfigUtility
{
string LogFilePath
{
get;
}
string GetAppSetting(string key);
}
ConfigUtility.cs
using System;
using System.Configuration;
public class ConfigUtility : IConfigUtility
{
Configuration config = null;
public string LogFilePath
{
get
{
return GetAppSetting(#"Code to read the log file path");
}
}
public ConfigUtility()
{
var exeConfigPath = this.GetType().Assembly.Location;
try
{
config = ConfigurationManager.OpenExeConfiguration(exeConfigPath);
}
catch (Exception)
{
//handle error here.. means DLL has no satellite configuration file.
}
}
public virtual string GetAppSetting(string key)
{
if (config != null)
{
KeyValueConfigurationElement element = config.AppSettings.Settings[key];
if (element != null)
{
string value = element.Value;
if (!string.IsNullOrEmpty(value))
return value;
}
}
return string.Empty;
}
}
Now you can use the above ConfigUtility.cs and read your appsettings key from the App.config file
Related
I am attempting to retrieve the typical AssemblyInfo attributes from an executable file, but not from the currently executing assembly. I wish to 'look into' a program file (.exe) elsewhere on the drive that I have written in C#.NET and check the AssemblyProduct string.
This is fairly easy and straightforward when you're looking for this information from the currently executing assembly. However, apparently not so much when you attempt to pull it from an unloaded assembly.
When I use the following code, it returns "Microsoft® .NET Framework" instead of the Product name that I put in my AssemblyInfo.cs file.
Note: I use the System.Reflection.AssemblyName object to pull the version info e.g:AssemblyName.GetAssemblyName(pathToAssembly) and this works correctly, but I'm unable to pull my assembly's attributes using that class or by any means I've tried thus far. Is there some other special class, or what am I missing or doing incorrectly here?
public static string GetAppProdIDFromPath(string pathToForeignAssembly)
{
var atts = GetForeignAssemblyAttributes(pathToForeignAssembly);
var id = string.Empty;
foreach (var att in atts)
{
if (att.GetType() == typeof(AssemblyProductAttribute))
{
id = ((AssemblyProductAttribute)att).Product;
}
}
return id;
}
private static object[] GetForeignAssemblyAttributes(string pathToAssembly)
{
if(File.Exists(pathToAssembly))
{
try
{
var assm = System.Reflection.Assembly.LoadFrom(pathToAssembly);
return assm.GetType().Assembly.GetCustomAttributes(false);
}
catch(Exception ex)
{
// logger etc
}
}
else
{
throw...
}
return null;
}
As Duncanp mentioned, there is a bug in my code. Posting it for clarity and for anyone down the road who looks for the same solution:
public static string GetAppProdIDFromPath(string pathToForeignAssembly)
{
var atts = GetForeignAssemblyAttributes(pathToForeignAssembly);
var id = string.Empty;
foreach (var att in atts)
{
if (att.GetType() == typeof(AssemblyProductAttribute))
{
id = ((AssemblyProductAttribute)att).Product;
}
}
return id;
}
private static object[] GetForeignAssemblyAttributes(string pathToAssembly)
{
if(File.Exists(pathToAssembly))
{
try
{
var assm = System.Reflection.Assembly.LoadFrom(pathToAssembly);
return assm.GetCustomAttributes(false); // fixed line
}
catch(Exception ex)
{
// logger etc
}
}
else
{
throw...
}
return null;
}
I have a configuration class with all the parameters of my application, to acquire images from a scanner.
I have parameters like color/BW, resolution...
The parameters are changed often, so I'm searching a solution to write automatically when I save the parameters the changed parameters in the app.config file. And to do the reverted thing, write my class from the app.config at the init of the software.
Here are my two classes :
private void GetParameters() {
try
{
var appSettings = ConfigurationManager.AppSettings;
Console.WriteLine( ConfigurationManager.AppSettings["MyKey"]);
if (appSettings.Count == 0)
{
Console.WriteLine("AppSettings is empty.");
}
else
{
foreach (var key in appSettings.AllKeys)
{
Console.WriteLine("Key: {0} Value: {1}", key, appSettings[key]);
}
}
}
catch (ConfigurationErrorsException)
{
MessageBox.Show("Error reading app settings");
}
}
private void SetParameters(string key, string value)
{
try
{
Configuration configManager = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
KeyValueConfigurationCollection confCollection = configManager.AppSettings.Settings;
if (confCollection[key] == null)
{
confCollection.Add(key, value);
}
else
{
confCollection[key].Value = value;
}
configManager.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection(configManager.AppSettings.SectionInformation.Name);
}
catch (ConfigurationErrorsException)
{
MessageBox.Show("Error writing app settings");
}
}
I don't want to call the method for every parameter...
And there is my parameters class :
class ScannerParameters
{
public bool Color { get; set; }
public int Resolution{ get; set; }
public string FilePath { get; set; }
public TypeScan TypeScan { get; set; }
public string TextTest{ get; set; }
}
The question can be translated into how do I save an object into some kind of persistence?
Either use a database (seems like an overkill) or serialize it using a serializer or simply write it all down into a text file yourself. Using json serialization, serializing your ScannerParameters and then writing that into a file would seem most appropriate.
Using newtonsoft json, which is defacto standard for .net there's nice examples # http://www.newtonsoft.com/json/help/html/SerializingJSON.htm
In your case you would do:
// our dummy scannerParameters objects
var parameters = new ScannerParameters();
// let's serialize it all into one string
string output = JsonConvert.SerializeObject(paramaters);
// let's write all that into a settings text file
System.IO.File.WriteAllText("parameters.txt", output);
// let's read the file next time we need it
string parametersJson = System.IO.File.ReadAllText("parameters.txt);
// let's deserialize the parametersJson
ScannerParameters scannerParameters = JsonConvert.DeserializeObject<ScannerParameters>(parametersJson);
I've been trying to create some custom check-in policies in TFS 2010 recently, I created a DLL and added a registry key as outlined in the following link:
https://msdn.microsoft.com/en-us/library/bb668980.aspx
(Note: I already know this link is retired, but I'm unable to do it in the updated fashion indicated here: http://blogs.msdn.com/b/jimlamb/archive/2010/03/31/how-to-implement-package-and-deploy-custom-check-in-policy-for-tfs-2010.aspx)
I didn't necessarily expect my own DLL to work correctly from the start in a functional sense, but Step 4 only shows the out of the box policies, mine is not available.
What could be causing mine to not appear? Below is the code I'm using (I've removed what it actually does as I don't think that's important)
[Serializable]
public class CheckInPolicy : PolicyBase
{
private string _status = string.Empty;
public override string Description
{
get { return "My description"; }
}
public override string Type
{
get { return "My type"; }
}
public override string TypeDescription
{
get { return "My type description"; }
}
public override bool Edit(IPolicyEditArgs policyEditArgs)
{
return true;
}
public override PolicyFailure[] Evaluate()
{
// Check if the check in meets the policy rules
// ...
// ...
// ...
}
public override void Activate(PolicyFailure failure)
{
base.Activate(failure);
}
public override void DisplayHelp(PolicyFailure failure)
{
MessageBox.Show(
"My policy help notification", "Policy Help");
}
}
This link mentioned that the policy needs to be placed in a different registry path if the OS is x64, but the server I'm using is a 32-bit Windows Server 2003 machine. The key is placed in:
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\TeamFoundation\SourceControl\Checkin Policies]
I guess you have a 64-bit machine, in that case the correct registry entry would be the 32-bit path:
[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\11.0\TeamFoundation\SourceControl\Checkin Policies
In addition to this, you usually need to build the Policy for each version of Visual Studio, as it also has references to the appropriate TeamExplorer libraries.
EDIT:
You can prohibit developers from overriding policies, if you extend your server side with a plugin, that declines all checkins with overridden policies.
You need to place the DLL under this TFS path (of course depending on your version):
Program Files\Microsoft Team Foundation Server 11.0\Application Tier\Web Services\bin\Plugins\
Here the code of this plugin class (some of the referenced DLLs can only be found on TFS servers). We allow to override if all files are in published folder, but you can of course define your own exclusions:
using System;
using Microsoft.TeamFoundation.Common;
using Microsoft.TeamFoundation.Framework.Server;
using Microsoft.TeamFoundation.VersionControl.Server;
using System.IO;
using System.Text.RegularExpressions;
using Microsoft.TeamFoundation.Framework.Server.Alm;
namespace TFS.CheckinPolicyOverrideEventHandler
{
public sealed class CheckinPolicyOverrideEventHandler : ISubscriber
{
public Type[] SubscribedTypes()
{
return new Type[1] { typeof(CheckinNotification) };
}
public EventNotificationStatus ProcessEvent(TeamFoundationRequestContext requestContext, NotificationType notificationType, object notificationEventArgs,
out int statusCode, out string statusMessage, out ExceptionPropertyCollection properties)
{
statusCode = 0;
properties = null;
statusMessage = String.Empty;
try
{
if (notificationType == NotificationType.DecisionPoint && notificationEventArgs is CheckinNotification)
{
CheckinNotification ev = notificationEventArgs as CheckinNotification;
if (ev != null && ev.PolicyOverrideInfo != null)
{
if (ev.PolicyOverrideInfo.PolicyFailures != null)
{
// One or more of the checkin policies have been overridden
// If all the files being checked in are in the published folder, then allow overridding the policies since those are installation packages
foreach (string file in ev.GetSubmittedItems(null))
{
if (!Regex.IsMatch(file, #"/published", RegexOptions.IgnoreCase) &&
!Regex.IsMatch(Path.GetDirectoryName(file), #"/published", RegexOptions.IgnoreCase))
{
statusCode = -1;
break;
}
}
if (statusCode != 0)
{
// One or more of the checkin policies have been overridden and not all files are installation files (in the published folder)
statusMessage = Resource.CheckinCancelledStatusMessage;
foreach (PolicyFailureInfo policy in ev.PolicyOverrideInfo.PolicyFailures)
{
statusMessage = String.Concat(statusMessage, "\n > ", policy.PolicyName, ": ", policy.Message);
}
return EventNotificationStatus.ActionDenied;
}
}
}
return EventNotificationStatus.ActionPermitted;
}
}
catch (Exception exception)
{
// decide what you want to do, if exception occurs
}
return EventNotificationStatus.ActionPermitted;
}
public string Name
{
get { return "TFS.CheckinPolicyOverrideEventHandler"; }
}
public SubscriberPriority Priority
{
get { return SubscriberPriority.Normal; }
}
}
}
I have Two projects in my Solution. Let's say Project A and Project B.
Project A
It's the main project and has settings. I have a check box to give user an option to "Repeat" track(s). This project can also access PROJECT B's public instances.
Project B
It's the BackgroundAudioAgent and has it's own settings. This project doesn't have access to PROJECT A settings. Therefore, in PROJECT A , I need to access the settings of PROJECT B and save it there. So that, when the "Repeat" is enabled, the agent restarts playing.
PROBLEM
I am unable to save the settings (in other words, the settings are saved, but it does not take any affect) when the BackgroundAudioPlayer's instance is running. I always have to close the instance, and when I do that, the settings can be changed.
QUESTION
What is the most efficient way to do what I am trying to do?
How can I save the settings in the IsolatedStorage without closing the BackgroundAudioPlayer's instance? (as I don't want to interrupt any track being played).
CODE: What I have to do to save settings.
public bool SettingAudioRepeat
{
get
{
return GetValueOrDefault<bool>(SettingAudioRepeatKeyName, SettingAudioRepeatDefault);
}
set
{
if (AddOrUpdateValue(SettingAudioRepeatKeyName, value))
{
bool resumePlay = false;
try
{
if (BackgroundAudioPlayer.Instance.PlayerState != PlayState.Shutdown)
{
BackgroundAudioPlayer.Instance.Close();
resumePlay = true;
}
}
catch { }
TaskEx.Delay(300);
IQR_Settings iqrSet = new IQR_Settings();
iqrSet.SettingAudioRepeat = value;
iqrSet.Save(); //Saving the settings for Project B
Save(); //Saving the settings for Project A
try
{
if (resumePlay)
BackgroundAudioPlayer.Instance.Play(); //It starts all from scracth
}
catch { }
}
}
public T GetValueOrDefault<T>(string Key, T defaultValue)
{
T value;
// If the key exists, retrieve the value.
if (settings.Contains(Key))
{
value = (T)settings[Key];
}
// Otherwise, use the default value.
else
{
value = defaultValue;
}
return value;
}
CODE: What I simply want to do.
public bool SettingAudioRepeat
{
get
{
return GetValueOrDefault<bool>(SettingAudioRepeatKeyName, SettingAudioRepeatDefault);
}
set
{
if (AddOrUpdateValue(SettingAudioRepeatKeyName, value))
{
IQR_Settings iqrSet = new IQR_Settings();
iqrSet.SettingAudioRepeat = value;
iqrSet.Save(); //Saving the settings for Project B
Save(); //Saving the settings for Project A
}
}
I agree that Background Audio is a breast. Whenever using any background agent you cannot rely on the ApplicationSettings to be synced. If you want to have settings saved and accessed from the UI (app) and background (audio agent) you should save a file. You can serialize the settings using Json.Net and save a file to a known location. Here is sample of what is might look like
// From background agent
var settings = Settings.Load();
if(settings.Foo)
{
// do something
}
And here is a sample Settings File. The settings would need to be saved on a regular basis.
public class Settings
{
private const string FileName = "shared/settings.json";
private Settings() { }
public bool Foo { get; set; }
public int Bar { get; set; }
public static Settings Load()
{
var storage = IsolatedStorageFile.GetUserStoreForApplication();
if (storage.FileExists(FileName) == false) return new Settings();
using (var stream = storage.OpenFile(FileName, FileMode.Open, FileAccess.Read))
{
using (var reader = new StreamReader(stream))
{
string json = reader.ReadToEnd();
if (string.IsNullOrEmpty(json) == false)
{
return JsonConvert.DeserializeObject<Settings>(json);
}
}
}
return new Settings();
}
public void Save()
{
var storage = IsolatedStorageFile.GetUserStoreForApplication();
if(storage.FileExists(FileName)) storage.DeleteFile(FileName);
using (var fileStream = storage.CreateFile(FileName))
{
//Write the data
using (var isoFileWriter = new StreamWriter(fileStream))
{
var json = JsonConvert.SerializeObject(this);
isoFileWriter.WriteLine(json);
}
}
}
}
I personally have a FileStorage class that I use for saving/loading data. I use it everywhere. Here it is (and it does use the Mutex to prevent access to the file from both background agent and app). You can find the FileStorage class here.
I want to use a custom path for a user.config file, rather than have .NET read it from the default location.
I am opening the file like this:
ExeConfigurationFileMap configMap = new ExeConfigurationFileMap();
configMap.ExeConfigFilename = String.Format("{0}\\user.config",AppDataPath);
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(configMap, ConfigurationUserLevel.PerUserRoamingAndLocal);
But I can't figure out how to actually read settings out of it, I get a compile error saying that the values are inaccessible when I try to get a value through AppData or ConfigurationSection.
Do I need to create some sort of a wrapper class to consume the data properly?
I was recently tasked with a similar problem, I had to change the location of where settings files were read from the default location in AppData to the Application directory. My solution was to create my own settings files that derived from ApplicationSettingsBase which specified a custom SettingsProvider. While the solution felt like overkill at first, I've found it to be more flexible and maintainable than I had anticipated.
Update:
Sample Settings File:
public class BaseSettings : ApplicationSettingsBase
{
protected BaseSettings(string settingsKey)
{ SettingsKey = settingsKey.ToLower(); }
public override void Upgrade()
{
if (!UpgradeRequired)
return;
base.Upgrade();
UpgradeRequired = false;
Save();
}
[SettingsProvider(typeof(MySettingsProvider)), UserScopedSetting]
[DefaultSettingValue("True")]
public bool UpgradeRequired
{
get { return (bool)this["UpgradeRequired"]; }
set { this["UpgradeRequired"] = value; }
}
}
Sample SettingsProvider:
public sealed class MySettingsProvider : SettingsProvider
{
public override string ApplicationName { get { return Application.ProductName; } set { } }
public override string Name { get { return "MySettingsProvider"; } }
public override void Initialize(string name, NameValueCollection col)
{ base.Initialize(ApplicationName, col); }
public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection propertyValues)
{
// Use an XmlWriter to write settings to file. Iterate PropertyValueCollection and use the SerializedValue member
}
public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection props)
{
// Read values from settings file into a PropertyValuesCollection and return it
}
static MySettingsProvider()
{
appSettingsPath_ = Path.Combine(new FileInfo(Application.ExecutablePath).DirectoryName, settingsFileName_);
settingsXml_ = new XmlDocument();
try { settingsXml_.Load(appSettingsPath_); }
catch (XmlException) { CreateXmlFile_(settingsXml_); } //Invalid settings file
catch (FileNotFoundException) { CreateXmlFile_(settingsXml_); } // Missing settings file
}
}
A few improvements:
1) Load it up a bit simpler, no need for the other lines:
var config = ConfigurationManager.OpenExeConfiguration(...);
2) Access AppSettings properly:
config.AppSettings.Settings[...]; // and other things under AppSettings
3) If you want a custom configuration section, use this tool: http://csd.codeplex.com/
I never ended up getting the Configuration Manager approach working. After spending a half day muddling with no progress, I decided to roll my own solution as my needs are basic.
Here is the solution I came up with in the end:
public class Settings
{
private XmlDocument _xmlDoc;
private XmlNode _settingsNode;
private string _path;
public Settings(string path)
{
_path = path;
LoadConfig(path);
}
private void LoadConfig(string path)
{
//TODO: add error handling
_xmlDoc = null;
_xmlDoc = new XmlDocument();
_xmlDoc.Load(path);
_settingsNode = _xmlDoc.SelectSingleNode("//appSettings");
}
//
//use the same structure as in .config appSettings sections
//
public string this[string s]
{
get
{
XmlNode n = _settingsNode.SelectSingleNode(String.Format("//add[#key='{0}']", s));
return n != null ? n.Attributes["value"].Value : null;
}
set
{
XmlNode n = _settingsNode.SelectSingleNode(String.Format("//add[#key='{0}']", s));
//create the node if it doesn't exist
if (n == null)
{
n=_xmlDoc.CreateElement("add");
_settingsNode.AppendChild(n);
XmlAttribute attr =_xmlDoc.CreateAttribute("key");
attr.Value = s;
n.Attributes.Append(attr);
attr = _xmlDoc.CreateAttribute("value");
n.Attributes.Append(attr);
}
n.Attributes["value"].Value = value;
_xmlDoc.Save(_path);
}
}
}