在.NET应用程序中使字符串持久化

7

我正在尝试在使用C++编写的.NET应用程序中使字符串持久化(即文件路径),并在应用程序启动时读取它,在退出时写入它。

我正在努力寻找正确的方法。网络搜索指向了ConfigurationSettings和ConfigurationManager对象。似乎第一个是只读的,而第二个在Configuration参考文献中未找到(框架3.5)。

我知道可以执行显式读取/写入到注册表或外部文件,但我更喜欢一种更标准的方式。我不希望这需要超过两行代码。

我是否走上了正确的道路?

3个回答

3
使用VS2008和.NET Framework 3.5时,如果应用程序安装在%ProgramFiles%或%ProgramFiles(x86)%的子文件夹中,则无法创建所需的清单来修改app.config,因为它们受操作系统的特殊保护,并且如果没有以提升的进程身份运行,则无法写入到注册表的HKLM节点。我认为我会将默认值和一个布尔值编码到app.config中,告诉是否在便携模式下运行应用程序。从app.config中读取默认值,用user.config中的值覆盖变量(如果存在),并在便携模式下将值写入user.config,在非便携模式下将其写入app.config。在自定义类中,独立于框架提供的任何差劲支持(无法写入app.config,无混合模式)... 我认为这需要大约250行代码而不是2行,但它起作用了(抱歉,这是C#,但您可能知道如何将其适应为C++)。
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml.Linq;

namespace DesktopApp1 {

    static class Program {

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main() {
            Config myConfig = new Config();
            myConfig.Load();

            //Change settings during the livetime of the application
            myConfig.SomethingPath = @"C:\Temp\Foo\TestUser.dat";
            myConfig.PortableMode = false;

            //Write it when closing the application
            myConfig.Save();

        }

    }

    internal class Config {

        //Private Fields
        private AppOrUserConfig _Config;
        private Boolean _IsUserConfig;
        private String _AppConfigPath;
        private String _UserConfigPath;

        public void Load() {
            AppOrUserConfig myDefaultConfig = new AppOrUserConfig();
            AppOrUserConfig myAppConfig = new AppOrUserConfig(AppConfigPath, myDefaultConfig);
            if (myAppConfig.PortableMode) {
                _Config = myAppConfig;
                _IsUserConfig = false;
            } else {
                _Config = new AppOrUserConfig(UserConfigPath, myAppConfig);
                _IsUserConfig = true;
            }
        }

        public Boolean Save() {
            CheckLoad();
            String myFilePath = IsUserConfig ? UserConfigPath : AppConfigPath;
            try {
                String myContent = _Config.XmlContent;
                String myFolder = Path.GetDirectoryName(myFilePath);
                Directory.CreateDirectory(myFolder);
                File.Delete(myFilePath);
                File.WriteAllText(myFilePath, myContent, new UTF8Encoding(true));
                return true;
            } catch {
            }
            return false;
        }

        public Boolean PortableMode {
            get {
                CheckLoad();
                return _Config.PortableMode;
            }
            set {
                CheckLoad();
                if (PortableMode == value) return;
                if (value) {
                    _Config.PortableMode = true;
                    _IsUserConfig = false;
                    Save();
                } else {
                    String myPath = SomethingPath;
                    _Config.PortableMode = false;
                    Save();
                    Load();
                    SomethingPath = myPath;
                }
            }
        }

        public String SomethingPath {
            get {
                CheckLoad();
                return _Config.SomethingPath;
            }
            set {
                CheckLoad();
                _Config.SomethingPath = value;
            }
        }

        private String AppConfigPath {
            get {
                String myResult = _AppConfigPath;
                if (myResult == null) {
                    myResult = Assembly.GetEntryAssembly().EntryPoint.DeclaringType.Module.FullyQualifiedName + ".config";
                    _AppConfigPath = myResult;
                }
                return myResult;
            }
        }

        private String UserConfigPath {
            get {
                String myResult = _UserConfigPath;
                if (myResult == null) {
                    myResult = Path.Combine(Environment.ExpandEnvironmentVariables(@"%LOCALAPPDATA%\Cragin\FooApp"), Path.GetFileName(AppConfigPath));
                    _UserConfigPath = myResult;
                }
                return myResult;
            }
        }

        private Boolean IsUserConfig {
            get {
                return _IsUserConfig;
            }
        }

        private void CheckLoad() {
            if (_Config == null) throw new InvalidOperationException(@"Call method ""Load()"" first.");
        }

    }

    internal class AppOrUserConfig {

        //Private Fields
        private XDocument _Xml;

        //Constructors

        public AppOrUserConfig() {
            _Xml = XDocument.Parse(@"<?xml version=""1.0"" encoding=""utf-8""?>
                                        <configuration>
                                        <startup>
                                            <supportedRuntime version=""v2.0.50727""/>
                                        </startup>
                                        <appSettings>
                                            <add key=""PortableMode"" value=""Off""/>
                                            <add key=""SomethingPath"" value=""C:\ProgramData\Cragin\SomeLibrary""/>
                                        </appSettings>
                                        </configuration>");
        }

        public AppOrUserConfig(String filePath, AppOrUserConfig defaultValue) : this() {
            XDocument myXml = null;
            try {
                myXml = XDocument.Parse(File.ReadAllText(filePath));
            } catch {
                return;
            }
            AppOrUserConfig myDummy = new AppOrUserConfig(myXml, defaultValue);
            PortableMode = myDummy.PortableMode;
            SomethingPath = myDummy.SomethingPath;
        }

        public AppOrUserConfig(XDocument xml, AppOrUserConfig defaultValue) : this() {
            if (defaultValue == null) defaultValue = new AppOrUserConfig();
            if (xml == null) {
                PortableMode = defaultValue.PortableMode;
                SomethingPath = defaultValue.SomethingPath;
                return;
            }
            AppOrUserConfig myDummy = new AppOrUserConfig();
            myDummy._Xml = xml;
            PortableMode = myDummy.GetPortableMode(defaultValue.PortableMode);
            SomethingPath = myDummy.GetSomethingPath(defaultValue.SomethingPath);
        }

        public Boolean PortableMode {
            get {
                return GetPortableMode(false);
            }
            set {
                (from e in _Xml.Element("configuration").Element("appSettings").Elements("add") where (string)e.Attribute("key") == "PortableMode" select e).Last().Attribute("value").Value = value ? "on" : "off";
            }
        }

        public String SomethingPath {
            get {
                return GetSomethingPath(@"C:\ProgramData\Cragin\SomeLibrary");
            }
            set {
                (from e in _Xml.Element("configuration").Element("appSettings").Elements("add") where (string)e.Attribute("key") == "SomethingPath" select e).Last().Attribute("value").Value = value ?? "";
            }
        }

        public String XmlContent {
            get {
                return _Xml.ToString(SaveOptions.None);
            }
        }

        private Boolean GetPortableMode(Boolean defaultValue) {
            try {
                String myString = (from e in _Xml.Element("configuration").Element("appSettings").Elements("add") where (string)e.Attribute("key") == "PortableMode" select e).Last().Attribute("value").Value;
                return ToBoolean(myString);
            } catch {
                PortableMode = defaultValue;
                return defaultValue;
            }
        }

        private String GetSomethingPath(String defaultValue) {
            try {
                return (from e in _Xml.Element("configuration").Element("appSettings").Elements("add") where (string)e.Attribute("key") == "SomethingPath" select e).Last().Attribute("value").Value;
            } catch {
                SomethingPath = defaultValue;
                return defaultValue;
            }
        }

        private static Boolean ToBoolean(String value) {
            value = value.Trim();
            switch (value.Length) {
                case 1:
                    if (value[0] == '0') return false;
                    if (value[0] == '1') return true;
                    break;
                case 5:
                    if (value.ToLowerInvariant() == "false") return false;
                    break;
                case 4:
                    if (value.ToLowerInvariant() == "true") return true;
                    break;
                case 3:
                    if (value.ToLowerInvariant() == "off") return false;
                    break;
                case 2:
                    if (value.ToLowerInvariant() == "on") return true;
                    break;
            }
            throw new FormatException();
        }

    }

}

我希望这对您或其他人有所帮助。

感谢您的努力。这正是我想要避免的。正如在其他地方所写的那样,我已经实现了一个六行解决方案(4个用于加载,2个用于保存),但这仍然比我希望的要多。我也认为这更符合微软的意图,但在这种混乱中,你永远不知道。 - user1196549

1

它会将您的关键值保存在配置文件中。 - Luca Ziegler
@LucaZiegler:不,情况比那更复杂。涉及到两个文件。 - user1196549
1
Windows注册表专门为此而设计,非常简单易用。 - Señor CMasMas
@SeñorCMasMas:出于某种愚蠢的迷信,我想要避免那个。 - user1196549
Appsetting使用文件来存储值。您可能已经注意到了bin目录中的*.config文件。 - Dineshkumar
@DineshkumarPonnusamy:你不需要告诉我。看一下这个链接。涉及到两个配置文件,有一个特殊的初始化阶段。但其中一个文件似乎是不必要的... - user1196549

0

看起来你可能想要使用独立存储

对于桌面应用程序,独立存储是一种数据存储机制,通过定义将代码与保存的数据关联的标准化方式,提供隔离和安全性。标准化还提供其他好处。管理员可以使用设计用于操作独立存储的工具来配置文件存储空间、设置安全策略和删除未使用的数据。使用独立存储,你的代码不再需要唯一路径来指定文件系统中的安全位置,数据也受到其他仅具有独立存储访问权限的应用程序的保护。不需要硬编码信息来指示应用程序的存储区域位于何处。

如上所述,你不必担心各种计算机之间的潜在差异或者想出一个可能脆弱的专有解决方案。

你的解决方案,虽然可能有效,但似乎属于“专有”类别,并且可能不是配置管理器的最佳用法。


我绝对没有安全方面的顾虑,只追求编程便利和持久性。我已经实现了一个解决方案,但它让我花费了六行艰苦奋斗的代码。我觉得这对于如此基本的功能来说太可悲了。 - user1196549

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接