【问题标题】:.Net can app "B" read the user settings for app "A"?.Net 应用程序“B”可以读取应用程序“A”的用户设置吗?
【发布时间】:2015-04-02 18:43:51
【问题描述】:

标题说明了一切。 我的应用程序定义了一些用户设置,最终由 .NET 存储在该应用程序的用户特定文件中。

我有一个密切相关的支持应用程序需要读取其中一些相同的设置,但我不知道这是否可以通过编程方式实现?我见过的所有读取属性的示例都读取了与正在运行的应用程序相关的属性..

迈克尔

【问题讨论】:

  • 这是可能的,但如果 A 使用默认设置,这并不容易(路径中的哈希值很难预测)。是否可以让 A 将其自己的设置文件写入已知Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) 的子文件夹?
  • 是的,我认为这是最好的方法。

标签: .net settings


【解决方案1】:

当我过去遇到这种情况时,我所做的就是将我的设置保存到 CommonApplicationSettings 一个 xml 文件中。然后,我在 A 和 B 共享的公共 C.dll 中拥有设置的模型和读取器/写入器。

这是我制作的阅读器/编写器的示例类。它将设置保存在一个属性中,只要硬盘驱动器上的文件发生更改,就会加载设置的新副本并引发 PropertyChanged 事件。

两个程序都会创建Configuration 的副本,然后监听PropertyChanged 事件。如果它收到发生更改的信号,程序会从Configuration.Settings 重新读取设置并将它们用作它们的活动值。

public sealed class Configuration : INotifyPropertyChanged, IDisposable
{
    private static readonly ILog Logger = LogManager.GetLogger(typeof(Configuration));


    private readonly FileSystemWatcher _fileSystemWatcher;
    public string SettingsPath { get; private set; }
    private ConfigurationSettings _settings;

    public Configuration()
    {
        const string settingsFileName = "Settings.xml";
        const string programName = "App A";

        SettingsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), programName, settingsFileName);

        if (!File.Exists(SettingsPath))
        {
            CreateDefaultFile();
        }

        //Read in the settings.
        ReadSettingsFromFile();


        _fileSystemWatcher = new FileSystemWatcher(Path.GetDirectoryName(SettingsPath), settingsFileName);
        _fileSystemWatcher.BeginInit();
        _fileSystemWatcher.Changed += FileSystemWatcherOnChanged;
        _fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;
        _fileSystemWatcher.EndInit();
        _fileSystemWatcher.EnableRaisingEvents = true;
    }

    public ConfigurationSettings Settings
    {
        get { return _settings; }
        private set
        {
            if (Equals(value, _settings))
            {
                return;
            }
            _settings = value;
            OnPropertyChanged("Settings");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void ReadSettingsFromFile()
    {
        Logger.Debug("Reading settings from the file.");

        var serializer = new XmlSerializer(typeof(ConfigurationSettings));
        using (FileStream settingsStream = File.Open(SettingsPath, FileMode.Open, FileAccess.Read, FileShare.Read))
        {
            Settings = (ConfigurationSettings)serializer.Deserialize(settingsStream);
        }

    }

    private async void FileSystemWatcherOnChanged(object sender, FileSystemEventArgs fileSystemEventArgs)
    {
        _fileSystemWatcher.EnableRaisingEvents = false;

        Logger.Debug(new {fileSystemEventArgs.ChangeType, fileSystemEventArgs.FullPath, fileSystemEventArgs.Name});

        //Add a pause to allow for the file to be finished writing.
        await TaskEx.Delay(TimeSpan.FromSeconds(1));

        ReadSettingsFromFile();

        _fileSystemWatcher.EnableRaisingEvents = true;
    }

    /// <summary>
    ///     Creates the default settings file
    /// </summary>
    private void CreateDefaultFile()
    {

        var directoryInfo = Directory.CreateDirectory(Path.GetDirectoryName(SettingsPath));
        //add rights for other users to modify the directory.
        var security = directoryInfo.GetAccessControl();
        security.AddAccessRule(new FileSystemAccessRule(new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null), FileSystemRights.Modify, InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit, PropagationFlags.None, AccessControlType.Allow));
        directoryInfo.SetAccessControl(security);

        Settings = new ConfigurationSettings();
        Save();
    }

    private void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }

    public void Save()
    {
        bool oldState = false;

        if (_fileSystemWatcher != null)
        {
            oldState = _fileSystemWatcher.EnableRaisingEvents;
        }

        try
        {
            //Disable events while the value is written.
            if (_fileSystemWatcher != null)
            {
                _fileSystemWatcher.EnableRaisingEvents = false;
            }

            var serializer = new XmlSerializer(typeof(ConfigurationSettings));

            using (var filestream = new FileStream(SettingsPath, FileMode.Create, FileAccess.Write, FileShare.None))
            {
                serializer.Serialize(filestream, Settings);
            }
        }
        finally
        {
            if (_fileSystemWatcher != null)
            {
                _fileSystemWatcher.EnableRaisingEvents = oldState;
            }
        }
    }

    #region IDisposable Pattern

    private bool _disposed;

    /// <summary>
    ///     Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
    /// </summary>
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    /// <summary>
    ///     Finalizes this instance (called prior to garbage collection by the CLR)
    /// </summary>
    ~Configuration()
    {
        Dispose(false);
    }

    private void Dispose(bool fromUserCode)
    {
        if (!_disposed)
        {
            if (fromUserCode)
            {
                if (_fileSystemWatcher != null)
                {
                    _fileSystemWatcher.Dispose();
                }
            }
        }
        _disposed = true;
    }

    #endregion
}

这是我的 ConfigurationSettings 类的样子。

/// <summary>
/// Used as a container to represent the settings of the program
/// </summary>
[Serializable]
[XmlType(AnonymousType = false)]
public sealed class ConfigurationSettings : IEquatable<ConfigurationSettings>
{
    private TimeSpan _uploadInterval;
    private TimeSpan _pauseBetweenModules;
    private static readonly TimeSpan UploadIntervalDefault = new TimeSpan(0, 0, 30, 0);
    private static readonly TimeSpan PauseBetweenModulesDefault = new TimeSpan(0,0,0,5);
    private const int InitialBatchSizeDefault = 100;

    public ConfigurationSettings()
    {
        InitialBatchSize = InitialBatchSizeDefault;
        UploadInterval = UploadIntervalDefault;
        PauseBetweenModules = PauseBetweenModulesDefault;
        DatabaseInstances = new ObservableCollection<DatabaseInstance>();
        UploadPulseData = true;
    }

    /// <summary>
    /// Will upload the pulse finical data
    /// </summary>
    public bool UploadPulseData { get; set; }

    /// <summary>
    /// The batch size the auto windowing function will use for its initial value for the upload module.
    /// </summary>
    public int InitialBatchSize { get; set; }

    /// <summary>
    /// The ammount of time a pause should be inserted between modules to allow the program to do any work that
    /// has processing to do.
    /// </summary>
    [XmlIgnore] //Xml can not serialize a TimeSpan, so we use the hidden property PauseBetweenModulesInMilliseconds during serialization.
    public TimeSpan PauseBetweenModules
    {
        get { return _pauseBetweenModules; }
        set { _pauseBetweenModules = value; }
    }

    // Hidden property for serialization
    [XmlElement("PauseBetweenModulesInMilliseconds")]
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
    public long PauseBetweenModulesInMilliseconds
    {
        get { return _pauseBetweenModules.Ticks / TimeSpan.TicksPerMillisecond; }
        set { _pauseBetweenModules = new TimeSpan(value * TimeSpan.TicksPerMillisecond); }
    }

    /// <summary>
    /// The length of time between upload batches.
    /// </summary>
    [XmlIgnore] 
    public TimeSpan UploadInterval
    {
        get { return _uploadInterval; }
        set { _uploadInterval = value; }
    }

    // Hidden property for serialization
    [XmlElement("UploadIntervalInMinutes")]
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
    public long UploadIntervalInMinutes
    {
        get { return _uploadInterval.Ticks / TimeSpan.TicksPerMinute; }
        set { _uploadInterval = new TimeSpan(value * TimeSpan.TicksPerMinute);}
    }

    /// <summary>
    /// The databases to run uploads against.
    /// </summary>
    public List<DatabaseInstance> DatabaseInstances { get; set; }

    //We override Equals to make OnPropertyChanged less spammy, if the same file is loaded with the same settings it keeps the event from being raised.
    public bool Equals(ConfigurationSettings other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return _uploadInterval.Equals(other._uploadInterval) && 
               _pauseBetweenModules.Equals(other._pauseBetweenModules) &&
               InitialBatchSize == other.InitialBatchSize && 
               UploadPulseData == other.UploadPulseData &&
               DatabaseInstances.SequenceEqual(other.DatabaseInstances);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        return obj is ConfigurationSettings && Equals((ConfigurationSettings)obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            var hashCode = _uploadInterval.GetHashCode();
            hashCode = (hashCode*397) ^ _pauseBetweenModules.GetHashCode();
            hashCode = (hashCode*397) ^ InitialBatchSize;
            hashCode = (hashCode*397) ^ UploadPulseData.GetHashCode();
            if (DatabaseInstances != null)
            {
                foreach (var databaseInstance in DatabaseInstances)
                {
                    hashCode = (hashCode * 397) ^ (databaseInstance != null ? databaseInstance.GetHashCode() : 0);
                }
            }
            return hashCode;
        }
    }
}

【讨论】:

    【解决方案2】:

    使用ConfigurationManager.OpenExeConfiguration 方法可以非常直接地读取另一个应用程序的设置。

    例子:

    var config = System.Configuration.ConfigurationManager.OpenExeConfiguration(exePath);
    var x = config.AppSettings.Settings["setting"].Value;
    

    【讨论】:

    • 这看起来会从应用程序的配置文件中读取 .xml - 但我认为它不会获取任何“覆盖”,即从配置文件值更改的值和通过 Properties.Settings.Defaults.Save() 保存。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多