4

FileSystemWatcher を使用して app.config ファイルの変更を監視しています。また、設定ファイルへの書き込み。

これが私のコードです:

MyApplication はメイン プロジェクトであり、DataCache は MyApplication で参照されるクラス ライブラリです。

using System;
using System.Threading;
using System.IO;
namespace MyApplication
{  

  public class Program
  {
    public static string rootFolderPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
    public static string configFilePath = Path.Combine(rootFolderPath, "MyApplication.exe.config");static void Main(string[] args)
    {
        //First Time initializing the config file.
        ConfigFileMonitor.SetConfigFileAtRuntime(configFilePath);

        //Initializing the config file monitor on a separate thread.
        Thread monitorThread = new Thread(ConfigFileMonitor.BeginConfigFilesMonitor);
        monitorThread.Start();

        WriteToConfigFile();

    }

    static void WriteToConfigFile()
    {
        Console.WriteLine(String.Format("Hello {0} {1}", DataCache.Section1Data.FirstNameString, DataCache.Section1Data.LastNameString));

        string _firstName, _lastName = string.Empty;
        do
        {
            Console.WriteLine("");
            Console.Write("First Name : ");
            _firstName = Console.ReadLine();

            Console.Write("Last Name : ");
            _lastName = Console.ReadLine();

            if(_firstName.Length>0)
                DataCache.Section1Data.FirstNameString = _firstName;

            if(_lastName.Length>0)
                DataCache.Section1Data.LastNameString = _lastName;

            Console.WriteLine(String.Format("Hello {0} {1}", DataCache.Section1Data.FirstNameString, DataCache.Section1Data.LastNameString));
        }
        while (true);
    }
  }
}


using System;
using System.IO;

namespace MyApplication
{
    /// <summary>
    /// Monitors for any change in the app.config file
    /// </summary>
    public class ConfigFileMonitor
    {
         private static FileSystemWatcher _watcher;
         private static string _configFilePath = String.Empty;
         private static string _configFileName = String.Empty;

        /// <summary>
    /// Texts the files surveillance.
    /// </summary>
    public static void BeginConfigFilesMonitor()
    {
        try
        {
            string fileToMonitor = Program.configFilePath;
            if (fileToMonitor.Length == 0)
                Console.WriteLine("Incorrect config file specified to watch");
            else
            {
                _configFileName = Path.GetFileName(fileToMonitor);
                _configFilePath = fileToMonitor.Substring(0, fileToMonitor.IndexOf(_configFileName));
            }
            // Use FileWatcher to check and update only modified text files.
            WatchConfigFiles(_configFilePath, _configFileName);
        }
        catch (Exception e1)
        {
            Console.WriteLine(e1.Message);
        }
    }

    /// <summary>
    /// Watches the files.
    /// </summary>
    /// <param name="targetDir">The target dir.</param>
    /// <param name="filteredBy">The filtered by.</param>
    static void WatchConfigFiles(string targetDir, string filteredBy)
    {
        try
        {
            _watcher = new FileSystemWatcher();
            _watcher.Path = targetDir;
            _watcher.Filter = filteredBy;
            _watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite;
            _watcher.EnableRaisingEvents = true;

            _watcher.Changed += new FileSystemEventHandler(FileChanged);
            _watcher.WaitForChanged(WatcherChangeTypes.Changed);
        }
        catch (Exception e1)
        {
            Console.WriteLine(e1.Message);
        }
    }


    /// <summary>
    /// Handles the Changed event of the File control.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.IO.FileSystemEventArgs"/> instance containing the event data.</param>
    protected static void FileChanged(object sender, FileSystemEventArgs e)
    {
        try
        {
            _watcher.EnableRaisingEvents = false;

            string filechange = e.FullPath;

            Console.WriteLine("Configuration File: " + filechange + "changed");

            //Since the file is changed - We have reload the configuration settings again.
            SetConfigFileAtRuntime(Path.Combine(Program.rootFolderPath, "MyApplication.exe.config"));

            Console.WriteLine(String.Format("New Name : {0} {1}", DataCache.Section1Data.FirstNameString, DataCache.Section1Data.LastNameString));

        _watcher.EnableRaisingEvents = true;
        }
        catch (Exception e1)
        {
            Console.WriteLine(e1.Message);
        }
    }

    /// <summary>
    /// Sets the config file at runtime.
    /// </summary>
    /// <param name="configFilePath"></param>
    public static void SetConfigFileAtRuntime(string configFilePath)
    {
        string runtimeconfigfile;
        try
        {
            if (configFilePath.Length == 0)
                Console.WriteLine("Please specify a config file to read from ");
            else
            {
                runtimeconfigfile = configFilePath;
                _configFileName = Path.GetFileName(configFilePath);
                _configFilePath = configFilePath.Substring(0, configFilePath.IndexOf(_configFileName));
            }

            // Specify config settings at runtime.
            //System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
            DataCache.DataConfigSection.config = System.Configuration.ConfigurationManager.OpenExeConfiguration(configFilePath);
            //Similarly you can apply for other sections like SMTP/System.Net/System.Web etc..
            //But you have to set the File Path for each of these
            //config.AppSettings.File = runtimeconfigfile;

            //This doesn't actually going to overwrite you Exe App.Config file.
            //Just refreshing the content in the memory.
            DataCache.DataConfigSection.config.Save(System.Configuration.ConfigurationSaveMode.Modified);

            //Refreshing Config Section
            //ConfigurationManager.RefreshSection("appSettings");
            System.Configuration.ConfigurationManager.RefreshSection("MySectionGroup/Section1");                

            DataCache.Section1Data.configSection1 = null;
        }
        catch (Exception e1)
        {
            Console.WriteLine(e1.Message);
        }
    }
 }
}

<?xml version="1.0"?>
<configuration>
  <configSections>
    <sectionGroup name="MySectionGroup">
      <section name="Section1"  type="DataCache.DataConfigSection, DataCache"     allowLocation="false" allowDefinition="Everywhere"/>
      <section name="Section2"  type="DataCache.DataConfigSection, DataCache"     allowLocation="false" allowDefinition="Everywhere"/>
    </sectionGroup>
  </configSections>
  <MySectionGroup>
    <Section1>
      <name
        firstName ="Pierce"
        lastName ="Brosnan"
        />
    </Section1>
  </MySectionGroup>

<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>

</configuration>

DataCache クラスは次のとおりです。

using System;
namespace DataCache
{
    public sealed class DataConfigSection : System.Configuration.ConfigurationSection
    {
        public static System.Configuration.Configuration config = System.Configuration.ConfigurationManager.OpenExeConfiguration(
        System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetAssembly(typeof(DataConfigSection)).Location), "MyApplication.exe"));

    // Create a "name" element.
    [System.Configuration.ConfigurationProperty("name")]
    public NameElement Name
    {
        get
        {
            return (NameElement)this["name"];
        }
        set
        {
            this["name"] = value;
        }
    }

    // Define the "name" element
    // with firstName, secondName attributes.
    public class NameElement : System.Configuration.ConfigurationElement
    {
        [System.Configuration.ConfigurationProperty("firstName", DefaultValue = "abcd", IsRequired = true)]
        public String FirstName
        {
            get
            {
                return (String)this["firstName"];
            }
            set
            {
                this["firstName"] = value;
            }
        }

        [System.Configuration.ConfigurationProperty("lastName", DefaultValue = "xyz", IsRequired = true)]
        public String LastName
        {
            get
            {
                return (String)this["lastName"];
            }
            set
            {
                this["lastName"] = value;
            }
        }
    }
  }
}


namespace DataCache
{
    public class Section1Data
    {
        public static volatile DataConfigSection configSection1;
        private static object syncRoot = new System.Object();

    public const string Section1ConfigSectionName = "MySectionGroup/Section1";

    private Section1Data() { }

    public static DataConfigSection ConfigSection1
    {
        get
        {
            lock (syncRoot)
            {
                if (configSection1 == null)
                {
                    DataConfigSection.config = System.Configuration.ConfigurationManager.OpenExeConfiguration(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetAssembly(typeof(DataConfigSection)).Location), "MyApplication.exe"));
                    configSection1 = (DataConfigSection)DataConfigSection.config.GetSection(Section1ConfigSectionName);                   
                }
            }
            return configSection1;
        }
    }
    public static string FirstNameString
    {
        get
        {
            return ConfigSection1.Name.FirstName.ToString();
        }
        set
        {
            ConfigSection1.Name.FirstName = value;
            DataConfigSection.config.Save();
        }
    }
    public static string LastNameString
    {
        get
        {
            return ConfigSection1.Name.LastName.ToString();
        }
        set
        {
            ConfigSection1.Name.LastName = value;
            DataConfigSection.config.Save();
        }
    }
  }
}

問題は、コンソール入力から名前に変更が加えられた場合、FileChanged が 1 回呼び出され、その後の変更によって起動されないことです。さらに、構成ファイルに手動で変更を加えると、コンソールから変更が行われるまでイベントが発生し続けるまで問題なく動作します。

なぜこれが奇妙な振る舞いなのかわかりません。誰でもここで私を助けることができますか?

ありがとう、モニカ

4

2 に答える 2

2

短いバージョンは次のようになります。

this.Watcher = new FileSystemWatcher();
this.Watcher.Path = this.Dir;
this.Watcher.Filter = this.File;
this.Watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
this.Watcher.EnableRaisingEvents = true;
this.Watcher.Changed += this.OnFileChange;

ただし、これはもう少し複雑な (そして醜い) サンプルで、ライブ ソースから取り出したもので、追加の処理 (ファイルを読み取るだけでなく) が必要です。

public partial class FileModule
{
    private ConcurrentDictionary<string, InputFileInfo> inputFileList = new ConcurrentDictionary<string, InputFileInfo>();

    public FileModule()
    {
        this.InitializeInputFileWatchers();
    }

    [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
    public void EnableOrDisableRaisingEventsForFileWatchers(bool enable)
    {
        foreach (var el in this.inputFileList)
        {
            el.Value.Watcher.EnableRaisingEvents = enable;
        }
    }

    [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
    private void InitializeInputFileWatchers()
    {
        for (int i = 0; i < this.InputFiles.Count; i++)
        {
            if (File.Exists(this.InputFiles[i]))
            {
                InputFileInfo info = new InputFileInfo();
                info.Fullpath = ((FileModuleSettings)this.Settings).InputFiles[i];
                info.Watcher.Changed += this.OnFileChange;

                this.inputFileList.AddOrUpdate(info.Fullpath, info, (e, v) => { return info; });
            }
        }
    }

    [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
    private void OnFileChange(object source, FileSystemEventArgs e)
    {
        InputFileInfo info;
        if (this.inputFileList.TryGetValue(e.FullPath, out info))
        {
            DateTime lastWriteTime = System.IO.File.GetLastWriteTime(e.FullPath);
            if (info.LastHandledChange != lastWriteTime)
            {
                TimeSpan span = lastWriteTime.Subtract(info.LastHandledChange);
                if (span.Days == 0 && span.Hours == 0 && span.Minutes == 0 && span.Seconds == 0 && span.TotalMilliseconds < this.MinimumFileChangePeriod)
                {
                    // Event ignored due to required minimum file change period;
                }
                else
                {
                    info.LastHandledChange = lastWriteTime;
                    this.inputFileList.AddOrUpdate(e.FullPath, info, (a, v) => { return info; });

                    lock (this.readLockerObject)
                    {
                        this.ReadFile(e.FullPath);
                    }
                }
            }
        }
    }

    private bool ReadFile(string filepath, int count, bool directReading)
    {
        StreamReader sr = this.OpenStreamReader(file);

        if (sr != null)
        {
            string line;
            string[] split;
            int signalId;
            double value;

            while ((line = sr.ReadLine()) != null)
            {
                // do sth. with line
            }
        }
    }
}

internal class InputFileInfo : IDisposable
{
    public string Dir { get; private set; }

    public string File { get; private set; }

    public FileSystemWatcher Watcher { get; private set; }

    public DateTime LastHandledChange { get; set; }

    private string fullpath;

    public string Fullpath
    {
        get
        {
            return this.fullpath;
        }

        set
        {
            this.fullpath = BaseHelper.GetFullFilePath(value);
            this.Dir = Directory.GetParent(this.fullpath).ToString();
            this.File = this.fullpath.Replace(this.Dir + "\\", string.Empty);
            this.Watcher = new FileSystemWatcher();
            this.Watcher.Path = this.Dir;
            this.Watcher.Filter = this.File;
            this.Watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
            this.Watcher.EnableRaisingEvents = true;
        }
    }

    public void Dispose()
    {
        if (this.Watcher != null)
        {
            this.Watcher.Dispose();
            this.Watcher = null;
        }
    }
}
于 2013-01-02T14:47:48.773 に答える
0

非常に複雑なコードがあります。ウォッチャーのコードは約10行です。

ウォッチャーの初期化を別のスレッドにオフロードする必要はありません。メインスレッドで初期化します。ウォッチャーはバックグラウンドスレッドでファイルを監視し、変更が発生するとイベントを発生させるため、WaitForChanged()メソッドを呼び出す必要はありません。

私は自分の作業コードをチェックしてあなたのコードと比較しましたが、2つの違いは次のとおりです。1。私のフィルターは次のとおりです。NotifyFilters.FileName | NotifyFilters.DirectoryName 2. WaitForChanged()メソッドを呼び出さない3. [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]ウォッチャーと対話するすべてのメソッドに適用しました。

さらに、監視しているファイルがシステムディレクトリの1つにないことを確認してください...ウォッチャーはこれらのファイルでうまく機能しません。C:でファイルを見ようとしていたので、半日の間壁に頭をぶつけました。

于 2012-12-28T20:41:49.813 に答える