C# user.config 的自定义路径
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2265271/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Custom path of the user.config
提问by
I manage my application-settings using the setting-designer in VS2008.
我使用 VS2008 中的设置设计器管理我的应用程序设置。
"The exact path of the user.config files looks something like this:"
“user.config 文件的确切路径如下所示:”
<Profile Directory>\<Company Name>\
<App Name>_<Evidence Type>_<Evidence Hash>\<Version>\user.config
Is there to a way to customize this path? I would prefer something like this:
有没有办法自定义这条路径?我更喜欢这样的事情:
<Profile Directory>\<Company Name>\
<App Name>\<Version>\user.config
I noticed that white-spaces were replaced by underscores in the "Company Name" in the new created folder ("Test Company" --> "Test_Company"). I really wish to turn off this behavior.
我注意到在新创建的文件夹(“测试公司”->“Test_Company”)中的“公司名称”中,空格被下划线替换。我真的很想关闭这种行为。
You know, I could write a new XML-based setting-handler, but I would like to use the setting-designer.
您知道,我可以编写一个新的基于 XML 的设置处理程序,但我想使用设置设计器。
回答by Igor Lankin
You would have to implement your own SettingsProviderto customize the path.
您必须实现自己的SettingsProvider来自定义路径。
See this Client Settings FAQ
请参阅此客户端设置常见问题解答
Q: Why is the path so obscure? Is there any way to change/customize it?
A: The path construction algorithm has to meet certain rigorous requirements in terms of security, isolation and robustness. While we tried to make the path as easily discoverable as possible by making use of friendly, application supplied strings, it is not possible to keep the path totally simple without running into issues like collisions with other apps, spoofing etc.
The LocalFileSettingsProvider does not provide a way to change the files in which settings are stored. Note that the provider itself doesn't determine the config file locations in the first place - it is the configuration system. If you need to store the settings in a different location for some reason, the recommended way is to write your own SettingsProvider. This is fairly simple to implement and you can find samples in the .NET 2.0 SDK that show how to do this. Keep in mind however that you may run into the same isolation issues mentioned above .
问:为什么这条路这么模糊?有什么方法可以更改/自定义它吗?
A:路径构建算法在安全性、隔离性和健壮性方面必须满足一定的严格要求。虽然我们试图通过使用友好的、应用程序提供的字符串来使路径尽可能容易被发现,但不可能在不遇到与其他应用程序冲突、欺骗等问题的情况下保持路径完全简单。
LocalFileSettingsProvider 不提供更改存储设置的文件的方法。请注意,提供程序本身并不首先确定配置文件的位置——它是配置系统。如果出于某种原因需要将设置存储在不同的位置,推荐的方法是编写自己的 SettingsProvider。这实现起来相当简单,您可以在 .NET 2.0 SDK 中找到演示如何执行此操作的示例。但是请记住,您可能会遇到上述相同的隔离问题。
回答by Chuck Rostance
It hasn't been easy to find good info on implementing a custom settings provider so I'm including a complete implementation below (bottom.) The format of the user.config file is retained, as well as the functionality within the .settings designer. I'm certain there are parts that can be cleaned up a bit, so don't hassle me :)
找到关于实现自定义设置提供程序的好信息并不容易,所以我在下面(底部)包含了一个完整的实现。保留了 user.config 文件的格式,以及 .settings 设计器中的功能. 我确定有些部分可以清理一下,所以不要打扰我:)
Like others, I wanted to change the location of the user.config file and still get the fun and fanciness of working with the .settings files in the designer, including creating default values for new installations. Importantly, our app also already has other saved settings objects at a path (appData\local\etc) in which we have already decided, and we didn't want artifacts in multiple locations.
和其他人一样,我想更改 user.config 文件的位置,但仍然希望在设计器中使用 .settings 文件的乐趣和幻想,包括为新安装创建默认值。重要的是,我们的应用程序也已经在我们已经决定的路径 (appData\local\etc) 中保存了其他设置对象,并且我们不希望在多个位置出现工件。
The code is much longer than I'd like it to be, but there is no SHORT answer that I could find. Though it seems somewhat painful just to be able to control the path, creating a custom settings provider in itself is still pretty powerful. One could alter the follwing implementation to store the data just about anywhere including a custom encrypted file, database, or interact with a web serivice.
代码比我希望的要长得多,但我找不到简短的答案。尽管能够控制路径似乎有些痛苦,但创建自定义设置提供程序本身仍然非常强大。人们可以更改以下实现以将数据存储在几乎任何地方,包括自定义加密文件、数据库或与 Web 服务交互。
From what I've read, Microsoft does not intend on making the path to the config file configurable. I'll take their word for it when they say allowing that would be scary. See (this) post. Alas, if you want to do it yourself you must implement your own SettingsProvider.
据我所知,Microsoft 不打算使配置文件的路径可配置。当他们说允许那会很可怕时,我会相信他们的话。请参阅(此)帖子。唉,如果你想自己做,你必须实现你自己的SettingsProvider。
Here goes..
开始..
Add a referencein your project to System.Configuration, you'll need it to implement the SettingsProvider.
在您的项目中添加一个对System.Configuration的引用,您将需要它来实现 SettingsProvider。
Easy bit...Create a class which implements SettingsProvider, use ctrl+.to help you out.
Easy bit...创建一个实现 SettingsProvider 的类,使用ctrl+。来帮助你。
class CustomSettingsProvider : SettingsProvider
Another easy bit...Go to the code behind of your .settings file (there is a button in the designer) and decorate the class to point it to your implementation. This must be done to override the built in functionality, but it does not change how the designer works.(sorry the formatting here is weird)
另一个简单的地方...转到 .settings 文件后面的代码(设计器中有一个按钮)并装饰该类以将其指向您的实现。必须这样做以覆盖内置功能,但它不会改变设计器的工作方式。(抱歉这里的格式很奇怪)
[System.Configuration.SettingsProvider(typeof(YourCompany.YourProduct.CustomSettingsProvider))]
[System.Configuration.SettingsProvider(typeof(YourCompany.YourProduct.CustomSettingsProvider))]
public sealed partial class Settings
{
//bla bla bla
}
Getting the path:There is a property called "SettingsKey" (e.g. Properties.Settings.Default.SettingsKey) I used this to store the path. I made the following property.
获取路径:有一个名为“SettingsKey”的属性(例如 Properties.Settings.Default.SettingsKey),我用它来存储路径。我做了以下财产。
/// <summary>
/// The key this is returning must set before the settings are used.
/// e.g. <c>Properties.Settings.Default.SettingsKey = @"C:\temp\user.config";</c>
/// </summary>
private string UserConfigPath
{
get
{
return Properties.Settings.Default.SettingsKey;
}
}
Storing the settings values.I chose to use a dictionary. This will get used extensively in a bit. I created a struct as a helper.
存储设置值。我选择使用字典。这将得到广泛使用。我创建了一个结构体作为帮手。
/// <summary>
/// In memory storage of the settings values
/// </summary>
private Dictionary<string, SettingStruct> SettingsDictionary { get; set; }
/// <summary>
/// Helper struct.
/// </summary>
internal struct SettingStruct
{
internal string name;
internal string serializeAs;
internal string value;
}
The magic.You must override 2 methods, GetPropertyValuesand SetPropertyValues. GetPropertyValues recieves as a parameter what you see in the designer, you have to opportunity to update the values and return a new collection. SetPropertyValues is called when the user saves any changes to the values made at runtime, this is where I update the dictionary and write out the file.
魔法。您必须覆盖 2 个方法,GetPropertyValues和SetPropertyValues。GetPropertyValues 接收您在设计器中看到的参数作为参数,您必须有机会更新值并返回一个新集合。当用户保存对运行时所做的任何更改时,会调用 SetPropertyValues,这是我更新字典并写出文件的地方。
/// <summary>
/// Must override this, this is the bit that matches up the designer properties to the dictionary values
/// </summary>
/// <param name="context"></param>
/// <param name="collection"></param>
/// <returns></returns>
public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection)
{
//load the file
if (!_loaded)
{
_loaded = true;
LoadValuesFromFile();
}
//collection that will be returned.
SettingsPropertyValueCollection values = new SettingsPropertyValueCollection();
//iterate thought the properties we get from the designer, checking to see if the setting is in the dictionary
foreach (SettingsProperty setting in collection)
{
SettingsPropertyValue value = new SettingsPropertyValue(setting);
value.IsDirty = false;
//need the type of the value for the strong typing
var t = Type.GetType(setting.PropertyType.FullName);
if (SettingsDictionary.ContainsKey(setting.Name))
{
value.SerializedValue = SettingsDictionary[setting.Name].value;
value.PropertyValue = Convert.ChangeType(SettingsDictionary[setting.Name].value, t);
}
else //use defaults in the case where there are no settings yet
{
value.SerializedValue = setting.DefaultValue;
value.PropertyValue = Convert.ChangeType(setting.DefaultValue, t);
}
values.Add(value);
}
return values;
}
/// <summary>
/// Must override this, this is the bit that does the saving to file. Called when Settings.Save() is called
/// </summary>
/// <param name="context"></param>
/// <param name="collection"></param>
public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
{
//grab the values from the collection parameter and update the values in our dictionary.
foreach (SettingsPropertyValue value in collection)
{
var setting = new SettingStruct()
{
value = (value.PropertyValue == null ? String.Empty : value.PropertyValue.ToString()),
name = value.Name,
serializeAs = value.Property.SerializeAs.ToString()
};
if (!SettingsDictionary.ContainsKey(value.Name))
{
SettingsDictionary.Add(value.Name, setting);
}
else
{
SettingsDictionary[value.Name] = setting;
}
}
//now that our local dictionary is up-to-date, save it to disk.
SaveValuesToFile();
}
Complete solution.So here's the entire class which includes the constructor, Initialize, and helper methods. Feel free to cut/paste slice and dice.
完整的解决方案。所以这里是包含构造函数、Initialize 和辅助方法的整个类。随意剪切/粘贴切片和切块。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Reflection;
using System.Xml.Linq;
using System.IO;
namespace YourCompany.YourProduct
{
class CustomSettingsProvider : SettingsProvider
{
const string NAME = "name";
const string SERIALIZE_AS = "serializeAs";
const string CONFIG = "configuration";
const string USER_SETTINGS = "userSettings";
const string SETTING = "setting";
/// <summary>
/// Loads the file into memory.
/// </summary>
public CustomSettingsProvider()
{
SettingsDictionary = new Dictionary<string, SettingStruct>();
}
/// <summary>
/// Override.
/// </summary>
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
base.Initialize(ApplicationName, config);
}
/// <summary>
/// Override.
/// </summary>
public override string ApplicationName
{
get
{
return System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name;
}
set
{
//do nothing
}
}
/// <summary>
/// Must override this, this is the bit that matches up the designer properties to the dictionary values
/// </summary>
/// <param name="context"></param>
/// <param name="collection"></param>
/// <returns></returns>
public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection)
{
//load the file
if (!_loaded)
{
_loaded = true;
LoadValuesFromFile();
}
//collection that will be returned.
SettingsPropertyValueCollection values = new SettingsPropertyValueCollection();
//itterate thought the properties we get from the designer, checking to see if the setting is in the dictionary
foreach (SettingsProperty setting in collection)
{
SettingsPropertyValue value = new SettingsPropertyValue(setting);
value.IsDirty = false;
//need the type of the value for the strong typing
var t = Type.GetType(setting.PropertyType.FullName);
if (SettingsDictionary.ContainsKey(setting.Name))
{
value.SerializedValue = SettingsDictionary[setting.Name].value;
value.PropertyValue = Convert.ChangeType(SettingsDictionary[setting.Name].value, t);
}
else //use defaults in the case where there are no settings yet
{
value.SerializedValue = setting.DefaultValue;
value.PropertyValue = Convert.ChangeType(setting.DefaultValue, t);
}
values.Add(value);
}
return values;
}
/// <summary>
/// Must override this, this is the bit that does the saving to file. Called when Settings.Save() is called
/// </summary>
/// <param name="context"></param>
/// <param name="collection"></param>
public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
{
//grab the values from the collection parameter and update the values in our dictionary.
foreach (SettingsPropertyValue value in collection)
{
var setting = new SettingStruct()
{
value = (value.PropertyValue == null ? String.Empty : value.PropertyValue.ToString()),
name = value.Name,
serializeAs = value.Property.SerializeAs.ToString()
};
if (!SettingsDictionary.ContainsKey(value.Name))
{
SettingsDictionary.Add(value.Name, setting);
}
else
{
SettingsDictionary[value.Name] = setting;
}
}
//now that our local dictionary is up-to-date, save it to disk.
SaveValuesToFile();
}
/// <summary>
/// Loads the values of the file into memory.
/// </summary>
private void LoadValuesFromFile()
{
if (!File.Exists(UserConfigPath))
{
//if the config file is not where it's supposed to be create a new one.
CreateEmptyConfig();
}
//load the xml
var configXml = XDocument.Load(UserConfigPath);
//get all of the <setting name="..." serializeAs="..."> elements.
var settingElements = configXml.Element(CONFIG).Element(USER_SETTINGS).Element(typeof(Properties.Settings).FullName).Elements(SETTING);
//iterate through, adding them to the dictionary, (checking for nulls, xml no likey nulls)
//using "String" as default serializeAs...just in case, no real good reason.
foreach (var element in settingElements)
{
var newSetting = new SettingStruct()
{
name = element.Attribute(NAME) == null ? String.Empty : element.Attribute(NAME).Value,
serializeAs = element.Attribute(SERIALIZE_AS) == null ? "String" : element.Attribute(SERIALIZE_AS).Value,
value = element.Value ?? String.Empty
};
SettingsDictionary.Add(element.Attribute(NAME).Value, newSetting);
}
}
/// <summary>
/// Creates an empty user.config file...looks like the one MS creates.
/// This could be overkill a simple key/value pairing would probably do.
/// </summary>
private void CreateEmptyConfig()
{
var doc = new XDocument();
var declaration = new XDeclaration("1.0", "utf-8", "true");
var config = new XElement(CONFIG);
var userSettings = new XElement(USER_SETTINGS);
var group = new XElement(typeof(Properties.Settings).FullName);
userSettings.Add(group);
config.Add(userSettings);
doc.Add(config);
doc.Declaration = declaration;
doc.Save(UserConfigPath);
}
/// <summary>
/// Saves the in memory dictionary to the user config file
/// </summary>
private void SaveValuesToFile()
{
//load the current xml from the file.
var import = XDocument.Load(UserConfigPath);
//get the settings group (e.g. <Company.Project.Desktop.Settings>)
var settingsSection = import.Element(CONFIG).Element(USER_SETTINGS).Element(typeof(Properties.Settings).FullName);
//iterate though the dictionary, either updating the value or adding the new setting.
foreach (var entry in SettingsDictionary)
{
var setting = settingsSection.Elements().FirstOrDefault(e => e.Attribute(NAME).Value == entry.Key);
if (setting == null) //this can happen if a new setting is added via the .settings designer.
{
var newSetting = new XElement(SETTING);
newSetting.Add(new XAttribute(NAME, entry.Value.name));
newSetting.Add(new XAttribute(SERIALIZE_AS, entry.Value.serializeAs));
newSetting.Value = (entry.Value.value ?? String.Empty);
settingsSection.Add(newSetting);
}
else //update the value if it exists.
{
setting.Value = (entry.Value.value ?? String.Empty);
}
}
import.Save(UserConfigPath);
}
/// <summary>
/// The setting key this is returning must set before the settings are used.
/// e.g. <c>Properties.Settings.Default.SettingsKey = @"C:\temp\user.config";</c>
/// </summary>
private string UserConfigPath
{
get
{
return Properties.Settings.Default.SettingsKey;
}
}
/// <summary>
/// In memory storage of the settings values
/// </summary>
private Dictionary<string, SettingStruct> SettingsDictionary { get; set; }
/// <summary>
/// Helper struct.
/// </summary>
internal struct SettingStruct
{
internal string name;
internal string serializeAs;
internal string value;
}
bool _loaded;
}
}
回答by jlo-gmail
Building on Chucks excellent answer:
建立在 Chucks 的优秀答案上:
Implement a new partial class based on Settings.Designer.cs, so the Settings Designerdoes not wipe out the attribute when changes are made:
实现基于Settings.Designer.cs的新分部类,因此设置设计器在进行更改时不会清除该属性:
namespace Worker.Properties
{
[System.Configuration.SettingsProvider(
typeof(SettingsProviders.DllFileSettingsProvider<Settings>))]
internal sealed partial class Settings
{
}
}
I created the following class that can be put in an external project. It allows the config to be project.dll.config. By Inheriting and Overriding ConfigPathallows any path you like. I also added support for reading System.Collections.Specialized.StringCollection. This version is for Application settings.
我创建了以下可以放在外部项目中的类。它允许配置为project.dll.config。通过继承和覆盖ConfigPath允许您喜欢任何路径。我还添加了对读取System.Collections.Specialized.StringCollection 的支持。此版本用于应用程序设置。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Reflection;
using System.Xml.Linq;
using System.IO;
//http://stackoverflow.com/questions/2265271/custom-path-of-the-user-config
namespace SettingsProviders
{
public class DllFileSettingsProvider<Properties_Settings> : SettingsProvider where Properties_Settings : new()
{
const string NAME = "name";
const string SERIALIZE_AS = "serializeAs";
const string CONFIG = "configuration";
const string APPLICATION_SETTINGS = "applicationSettings";
const string SETTING = "setting";
/// <summary>
/// Loads the file into memory.
/// </summary>
public DllFileSettingsProvider()
{
SettingsDictionary = new Dictionary<string, SettingStruct>();
}
/// <summary>
/// Override.
/// </summary>
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
base.Initialize(ApplicationName, config);
}
/// <summary>
/// Override.
/// </summary>
public override string ApplicationName
{
get
{
return System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name;
}
set
{
//do nothing
}
}
/// <summary>
/// Must override this, this is the bit that matches up the designer properties to the dictionary values
/// </summary>
/// <param name="context"></param>
/// <param name="collection"></param>
/// <returns></returns>
public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection)
{
//load the file
if (!_loaded)
{
_loaded = true;
LoadValuesFromFile();
}
//collection that will be returned.
SettingsPropertyValueCollection values = new SettingsPropertyValueCollection();
//itterate thought the properties we get from the designer, checking to see if the setting is in the dictionary
foreach (SettingsProperty setting in collection)
{
SettingsPropertyValue value = new SettingsPropertyValue(setting);
value.IsDirty = false;
//need the type of the value for the strong typing
var t = Type.GetType(setting.PropertyType.FullName);
if (setting.PropertyType == typeof(System.Collections.Specialized.StringCollection))
{
var xml = SettingsDictionary[setting.Name].value;
var stringReader = new System.IO.StringReader(xml);
var xmlreader = System.Xml.XmlReader.Create(stringReader);
var ser = new System.Xml.Serialization.XmlSerializer(typeof(System.Collections.Specialized.StringCollection));
var obj = ser.Deserialize(xmlreader);
var col = (System.Collections.Specialized.StringCollection)obj;
value.PropertyValue = col;
}
else if (SettingsDictionary.ContainsKey(setting.Name))
{
value.SerializedValue = SettingsDictionary[setting.Name].value;
value.PropertyValue = Convert.ChangeType(SettingsDictionary[setting.Name].value, t);
}
else //use defaults in the case where there are no settings yet
{
value.SerializedValue = setting.DefaultValue;
value.PropertyValue = Convert.ChangeType(setting.DefaultValue, t);
}
values.Add(value);
}
return values;
}
/// <summary>
/// Must override this, this is the bit that does the saving to file. Called when Settings.Save() is called
/// </summary>
/// <param name="context"></param>
/// <param name="collection"></param>
public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
{
//grab the values from the collection parameter and update the values in our dictionary.
foreach (SettingsPropertyValue value in collection)
{
var setting = new SettingStruct()
{
value = (value.PropertyValue == null ? String.Empty : value.PropertyValue.ToString()),
name = value.Name,
serializeAs = value.Property.SerializeAs.ToString()
};
if (!SettingsDictionary.ContainsKey(value.Name))
{
SettingsDictionary.Add(value.Name, setting);
}
else
{
SettingsDictionary[value.Name] = setting;
}
}
//now that our local dictionary is up-to-date, save it to disk.
SaveValuesToFile();
}
/// <summary>
/// Loads the values of the file into memory.
/// </summary>
private void LoadValuesFromFile()
{
if (!File.Exists(ConfigPath))
{
//if the config file is not where it's supposed to be create a new one.
throw new Exception("Config file not found: " + ConfigPath);
}
//load the xml
var configXml = XDocument.Load(ConfigPath);
//get all of the <setting name="..." serializeAs="..."> elements.
var settingElements = configXml.Element(CONFIG).Element(APPLICATION_SETTINGS).Element(typeof(Properties_Settings).FullName).Elements(SETTING);
//iterate through, adding them to the dictionary, (checking for nulls, xml no likey nulls)
//using "String" as default serializeAs...just in case, no real good reason.
foreach (var element in settingElements)
{
var newSetting = new SettingStruct()
{
name = element.Attribute(NAME) == null ? String.Empty : element.Attribute(NAME).Value,
serializeAs = element.Attribute(SERIALIZE_AS) == null ? "String" : element.Attribute(SERIALIZE_AS).Value ,
value = element.Value ?? String.Empty
};
if (newSetting.serializeAs == "Xml")
{
var e = (XElement)element.Nodes().First();
newSetting.value = e.LastNode.ToString() ?? String.Empty;
};
SettingsDictionary.Add(element.Attribute(NAME).Value, newSetting);
}
}
/// <summary>
/// Creates an empty user.config file...looks like the one MS creates.
/// This could be overkill a simple key/value pairing would probably do.
/// </summary>
private void CreateEmptyConfig()
{
var doc = new XDocument();
var declaration = new XDeclaration("1.0", "utf-8", "true");
var config = new XElement(CONFIG);
var userSettings = new XElement(APPLICATION_SETTINGS);
var group = new XElement(typeof(Properties_Settings).FullName);
userSettings.Add(group);
config.Add(userSettings);
doc.Add(config);
doc.Declaration = declaration;
doc.Save(ConfigPath);
}
/// <summary>
/// Saves the in memory dictionary to the user config file
/// </summary>
private void SaveValuesToFile()
{
//load the current xml from the file.
var import = XDocument.Load(ConfigPath);
//get the settings group (e.g. <Company.Project.Desktop.Settings>)
var settingsSection = import.Element(CONFIG).Element(APPLICATION_SETTINGS).Element(typeof(Properties_Settings).FullName);
//iterate though the dictionary, either updating the value or adding the new setting.
foreach (var entry in SettingsDictionary)
{
var setting = settingsSection.Elements().FirstOrDefault(e => e.Attribute(NAME).Value == entry.Key);
if (setting == null) //this can happen if a new setting is added via the .settings designer.
{
var newSetting = new XElement(SETTING);
newSetting.Add(new XAttribute(NAME, entry.Value.name));
newSetting.Add(new XAttribute(SERIALIZE_AS, entry.Value.serializeAs));
newSetting.Value = (entry.Value.value ?? String.Empty);
settingsSection.Add(newSetting);
}
else //update the value if it exists.
{
setting.Value = (entry.Value.value ?? String.Empty);
}
}
import.Save(ConfigPath);
}
/// <summary>
/// The setting key this is returning must set before the settings are used.
/// e.g. <c>Properties.Settings.Default.SettingsKey = @"C:\temp\user.config";</c>
/// </summary>
public virtual string ConfigPath
{
get
{
var name = new Properties_Settings().GetType().Module.Name + ".config";
if (System.IO.File.Exists(name)==false)
{
System.Diagnostics.Trace.WriteLine("config file NOT found:" + name);
}
System.Diagnostics.Trace.WriteLine("config file found:" + name);
return name;
// return Properties.Settings.Default.SettingsKey;
}
}
/// <summary>
/// In memory storage of the settings values
/// </summary>
internal Dictionary<string, SettingStruct> SettingsDictionary { get; set; }
/// <summary>
/// Helper struct.
/// </summary>
internal struct SettingStruct
{
internal string name;
internal string serializeAs;
internal string value;
}
bool _loaded;
}
}
Make sure dependent projects include the project.dll.configfile by add a post-build event:
通过添加构建后事件确保依赖项目包含project.dll.config文件:
copy $(SolutionDir)Worker$(OutDir)Worker.dll.config $(TargetDir) /y
回答by Matt Chambers
Here is an easier, briefer alternative to creating a custom settings class: change your app's evidence so that the "url" part is a constant rather than being based on the location of executable. To do this, you need to modify the default AppDomain when the program starts. There are two parts: setting app.config to use your AppDomainManager, and creating an AppDomainManager and HostSecurityManager to customize the Url evidence. Sounds complicated but it's much simpler than creating a custom settings class. This only applies to unsigned assemblies. If you have a signed assembly, it's going to use that evidence instead of the Url. But the good news there is your path will always be constant anyway (as long as the signing key doesn't change).
这是创建自定义设置类的更简单、更简洁的替代方法:更改应用程序的证据,以便“url”部分是一个常量,而不是基于可执行文件的位置。为此,您需要在程序启动时修改默认的 AppDomain。有两个部分:设置 app.config 以使用您的 AppDomainManager,以及创建 AppDomainManager 和 HostSecurityManager 以自定义 Url 证据。听起来很复杂,但它比创建自定义设置类要简单得多。这仅适用于未签名的程序集。如果您有一个签名的程序集,它将使用该证据而不是 Url。但好消息是,无论如何,您的路径将始终保持不变(只要签名密钥不变)。
You can copy the code below and just replace the YourAppName
bits.
您可以复制下面的代码并替换YourAppName
位。
DefaultAppDomainManager.cs:
DefaultAppDomainManager.cs:
using System;
using System.Security;
using System.Security.Policy;
namespace YourAppName
{
/// <summary>
/// A least-evil (?) way of customizing the default location of the application's user.config files.
/// </summary>
public class CustomEvidenceHostSecurityManager : HostSecurityManager
{
public override HostSecurityManagerOptions Flags
{
get
{
return HostSecurityManagerOptions.HostAssemblyEvidence;
}
}
public override Evidence ProvideAssemblyEvidence(System.Reflection.Assembly loadedAssembly, Evidence inputEvidence)
{
if (!loadedAssembly.Location.EndsWith("YourAppName.exe"))
return base.ProvideAssemblyEvidence(loadedAssembly, inputEvidence);
// override the full Url used in Evidence to just "YourAppName.exe" so it remains the same no matter where the exe is located
var zoneEvidence = inputEvidence.GetHostEvidence<Zone>();
return new Evidence(new EvidenceBase[] { zoneEvidence, new Url("YourAppName.exe") }, null);
}
}
public class DefaultAppDomainManager : AppDomainManager
{
private CustomEvidenceHostSecurityManager hostSecurityManager;
public override void InitializeNewDomain(AppDomainSetup appDomainInfo)
{
base.InitializeNewDomain(appDomainInfo);
hostSecurityManager = new CustomEvidenceHostSecurityManager();
}
public override HostSecurityManager HostSecurityManager
{
get
{
return hostSecurityManager;
}
}
}
}
app.config excerpt:
app.config excerpt:
<runtime>
<appDomainManagerType value="YourAppName.DefaultAppDomainManager" />
<appDomainManagerAssembly value="DefaultAppDomainManager, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</runtime>