C# で 指定ディレクトリ配下 に ファイル生成 されたことを 監視 する方法

0 件のコメント

単純にファイル生成されたかどうかを監視するのであれば FileSystemWathcer を利用するとよいと思います。 ちまたのコードでもそのようなものが多いハズです。

ただ… FileSystemWatcher だけだと、ファイル生成された瞬間にイベントが発生するため、大きなファイルを生成中だとファイルオープンできない問題があります。 なので、ファイル生成された後、開けるようになったらイベント発生させるように少し拡張した 「ファイル生成の監視クラス」 を作ってみました。 …と言っても、イベント発生時に生成されたファイルを開けるかどうかチェックして、無理であれば Timer でもう一度チェックする、といった単純なものです。

目次

仕様

単純には以下のような仕様のものを作ります。

  • 監視するディレクトリが指定できる
  • 監視するディレクトリはサブディレクトリも監視対象にするかどうか指定できる
  • 監視するファイルのフィルタ条件を指定できる
  • ファイル生成されて開けるようになったとき Fireイベント が発生する

ただ、実装上の問題から、 Fireイベント は別スレッドで実行される場合がある、といった注意点があります。

ファイル生成を監視するクラス の サンプルコード

ファイル生成を監視するクラス本体 "FileCreationTrigger"と、イベント発生時に受け渡されるイベント変数 "FileCreationEventArgs" のサンプルコードを以下に掲載します。

FileCreationTrigger.cs

namespace garafu
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Timers;

    /// <summary>
    /// ファイル生成を監視
    /// </summary>
    public class FileCreationTrigger : IDisposable
    {
        #region ' フィールド
        // ---------------------------------------
        //  フィールド
        // ---------------------------------------

        /// <summary>
        /// 監視する対象のフィルタ条件
        /// </summary>
        private string filter = string.Empty;

        /// <summary>
        /// サブディレクトリを検索するかどうか
        /// </summary>
        private bool includeSubdirectories = true;

        /// <summary>
        /// 監視する間隔
        /// </summary>
        private double interval = 0;

        /// <summary>
        /// 監視する対象のフォルダパス
        /// </summary>
        private string path = string.Empty;

        /// <summary>
        /// Timerクラスのインスタンス
        /// </summary>
        private Timer timer;

        /// <summary>
        /// FileSystemWatcherクラスのインスタンス
        /// </summary>
        private FileSystemWatcher watcher;

        /// <summary>
        /// 生成されたファイルでまだイベントを発生させていないファイルのリスト
        /// </summary>
        private List<string> watches = new List<string>();

        #endregion
        #region ' コンストラクタ
        // ---------------------------------------
        //  フィールド
        // ---------------------------------------

        /// <summary>
        /// FileCreationTrigger クラスのインスタンスを生成、初期化します。
        /// </summary>
        public FileCreationTrigger()
        {
            this.InitializeInstance(null, null, null, null);
        }

        /// <summary>
        /// 監視ディレクトリ、監視ファイルのフィルタ条件、サブディレクトリを監視するかどうか を指定して
        /// FileCreationTrigger クラスのインスタンスを生成、初期化します。
        /// </summary>
        /// <param name="path">監視ディレクトリ</param>
        /// <param name="fileter">監視ファイルのフィルタ条件</param>
        /// <param name="includeSubdirectories">サブディレクトリを監視するかどうか</param>
        public FileCreationTrigger(string path, string fileter, bool includeSubdirectories)
        {
            this.InitializeInstance(path, fileter, includeSubdirectories, null);
        }

        #endregion
        #region ' イベント
        // ---------------------------------------
        //  イベント
        // ---------------------------------------

        /// <summary>
        /// 監視対象ディレクトリに指定されたフィルタに該当するファイルが生成されたとき発生します。
        /// メインスレッドとは別スレッドで動作する場合があります。
        /// </summary>
        public event EventHandler<FileCreationEventArgs> Fire;

        #endregion
        #region ' プロパティ
        // ---------------------------------------
        //  プロパティ
        // ---------------------------------------

        /// <summary>
        /// 監視対象ファイルのフィルタ条件を取得または設定します。
        /// </summary>
        public string Filter
        {
            get
            {
                return this.filter;
            }

            set
            {
                this.filter = value;
                this.watcher.Filter = value;
            }
        }

        /// <summary>
        /// サブディレクトリを監視するかどうかを取得または設定します。
        /// </summary>
        public bool IncludeSubdirectories
        {
            get
            {
                return this.includeSubdirectories;
            }

            set
            {
                this.includeSubdirectories = value;
                this.watcher.IncludeSubdirectories = value;
            }
        }

        /// <summary>
        /// 監視間隔(msec)を取得または設定します。
        /// </summary>
        public double Interval
        {
            get
            {
                return this.interval;
            }

            set
            {
                this.interval = value;
                this.timer.Interval = value;
            }
        }

        /// <summary>
        /// 監視対象ディレクトリのパスを取得または設定します。
        /// </summary>
        public string Path
        {
            get
            {
                return this.path;
            }

            set
            {
                this.path = value;
                this.watcher.Path = value;
            }
        }
        #endregion
        #region ' メソッド
        // ---------------------------------------
        //  メソッド
        // ---------------------------------------

        /// <summary>
        /// インスタンスを廃棄します。
        /// </summary>
        public void Dispose()
        {
            this.watcher.Dispose();
            this.timer.Dispose();
        }

        /// <summary>
        /// Fireイベントの発生を開始します。
        /// </summary>
        public void Start()
        {
            this.watcher.EnableRaisingEvents = true;
            this.timer.Start();
        }

        /// <summary>
        /// Fireイベントの発生を停止します。
        /// </summary>
        public void Stop()
        {
            this.watcher.EnableRaisingEvents = false;
            this.timer.Stop();
        }

        /// <summary>
        /// FileSystemWatcherでCreatedイベントが発生したとき実行されます。
        /// </summary>
        /// <param name="sender">呼び出し元インスタンス</param>
        /// <param name="e">イベント引数</param>
        private void FileSystemWatcher_Created(object sender, FileSystemEventArgs e)
        {
            if (this.IsFileLocked(e.FullPath) == false)
            {
                if (this.watches.Contains(e.FullPath))
                {
                    this.watches.Remove(e.FullPath);
                }

                this.OnFire(e.FullPath);
            }
            else
            {
                if (this.watches.Contains(e.FullPath) == false)
                {
                    this.watches.Add(e.FullPath);
                }
            }
        }

        /// <summary>
        /// インスタンスを初期化します。
        /// </summary>
        /// <param name="path">監視ディレクトリのパス。デフォルトはカレントディレクトリ。</param>
        /// <param name="filter">監視する対象のフィルタ条件。デフォルトはすべてのファイル「*」。</param>
        /// <param name="includeSubdirectories">サブディレクトリを検索するかどうかデフォルトは true。</param>
        /// <param name="interval">監視する間隔(msec)。デフォルトは 1000 ミリ秒。</param>
        private void InitializeInstance(string path, string filter, bool? includeSubdirectories, double? interval)
        {
            // インスタンス生成
            this.watcher = new FileSystemWatcher();
            this.timer = new Timer();

            // 初期設定
            this.Path = string.IsNullOrEmpty(path) ? Environment.CurrentDirectory : path;
            this.Filter = string.IsNullOrEmpty(filter) ? "*" : filter;
            this.IncludeSubdirectories = includeSubdirectories.GetValueOrDefault(true);
            this.Interval = interval.GetValueOrDefault(1000);

            // イベントのアタッチ
            this.watcher.Created += new FileSystemEventHandler(this.FileSystemWatcher_Created);
            this.timer.Elapsed += new ElapsedEventHandler(this.Timer_Elapsed);
        }

        /// <summary>
        /// 指定されたファイルがロックされているかどうかを返します。
        /// </summary>
        /// <param name="path">検証したいファイルへのフルパス</param>
        /// <returns>ロックされているかどうか</returns>
        private bool IsFileLocked(string path)
        {
            FileStream stream = null;

            try
            {
                stream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
            }
            catch
            {
                return true;
            }
            finally
            {
                if (stream != null)
                {
                    stream.Close();
                }
            }

            return false;
        }

        /// <summary>
        /// Fireイベントを発生させます。
        /// </summary>
        /// <param name="fullpath">イベント発生源となったファイルへのフルパス</param>
        private void OnFire(string fullpath)
        {
            if (this.Fire != null)
            {
                this.Fire(this, new FileCreationEventArgs(fullpath));
            }
        }

        /// <summary>
        /// TimerでElapsedイベントが発生したとき実行されます。
        /// </summary>
        /// <param name="sender">呼び出し元インスタンス</param>
        /// <param name="e">イベント引数</param>
        private void Timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            foreach (var fullpath in this.watches)
            {
                if (this.IsFileLocked(fullpath) == false)
                {
                    this.watches.Remove(fullpath);
                    this.OnFire(fullpath);
                }
            }
        }

        #endregion
    }
}

FileCreationEventArgs.cs

namespace garafu
{
    using System;

    /// <summary>
    /// ファイル生成
    /// </summary>
    public class FileCreationEventArgs : EventArgs
    {
        /// <summary>
        /// 生成されたファイルへのフルパスを取得します。
        /// </summary>
        public string FullPath { get; private set; }

        /// <summary>
        /// パス情報を指定して、FileCreationEventArgs クラスのインスタンスを生成、初期化します。
        /// </summary>
        /// <param name="fullpath">パス情報</param>
        public FileCreationEventArgs(string fullpath)
        {
            this.FullPath = fullpath;
        }
    }
}

ファイル生成を監視するクラス の 利用例

ファイル生成を監視するクラスの利用方法は、インスタンス生成して Fireイベント に イベントハンドラ を接続し、 Startメソッド を呼び出すだけです。 以下にサンプルコードを掲載します。

namespace WpfApplication1
{
    using System;
    using System.IO;
    using System.Windows;
    using garafu;
    
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        private FileCreationTrigger trigger;

        public MainWindow()
        {
            this.InitializeComponent();
            
            this.trigger = new FileCreationTrigger();
            this.trigger.Fire += new EventHandler<FileCreationEventArgs>(this.Trigger_Fire);
            this.trigger.Start();
        }

        private void Trigger_Fire(object sender, FileCreationEventArgs e)
        {
            // ファイル生成されたときの処理
        }
    }
}