C#

【C#】FileSystemWatcherクラスの使い方と注意点

アプリケーションを作成している時にファイルが更新されたら、処理をさせたいことありませんか?

そういう場合は「FileSystemWatcherクラス」が使うと簡単にファイルの監視を行うことができます。

今回はFileSystemWatcherの使い方と注意点について紹介します。

FileSystemWatcherクラス

C#には、ファイルやディレクトリの作成・変更・削除を監視するためのFileSystemWatcherクラスが用意されています。

これを利用することで、特定のディレクトリにファイルが作成・変更・削除・名前の変更など状態変更が発生したタイミングでイベントを発火することができます。

プロパティの設定

FileSystemWatcherクラスを利用する際は、以下のプロパティを設定します。

Path
監視するディレクトリのパスを指定します。
Filter監視するファイルの拡張子を指定します。”*.*”は全てのファイルを監視します。”*.txt”はテキストファイルを監視します。
NotifyFilter監視する変更の種類を設定します。複数監視対象がある場合はORで組み合わせます。
IncludeSubdirectories指定したディレクトリのサブディレクトリを監視するかどうかを示すフラグを設定します。
EnableRaisingEvents監視を有効にするか無効にするかを設定します。”true”にすると監視が開始されます。

プロパティを使うことで監視対象とするファイルの範囲を指定することができます。

Filterプロパティはテキストファイル以外にも”*.png”や”*.csv”などの拡張子も使えます。EnableRaisingEventsプロパティはイベントを発生させたくない時は”false”にすることで監視を無効にすることができます。

NotifyFilterプロパティはNotifyFiltersの列挙体で定義されているものから必要なものをORで組み合わせて設定します。列挙体一覧は以下になります。

NotifyFiltersの列挙体(enum)
  • Attributes:ファイルまたはフォルダの属性
  • CreationTime:ファイルまたはフォルダが作成された時刻
  • DirectoryName:ディレクトリの名前
  • FileName:ファイルの名前
  • LastAccess:ファイルまたはフォルダへの最終アクセス日時
  • LastWrite:ファイルまたはフォルダへの最終書き込み日時
  • Security:ファイルまたはフォルダのセキュリティ設定
  • Size:ファイルまたはフォルダのサイズ

イベントの設定

NotifyFiltersプロパティで設定した監視対象が変更された時にイベントが発火されるように、イベントの設置を行います。設定できるイベントの種類は以下になります。

Createdファイルまたはディレクトリが作成された時にイベントが発生します。
Deletedファイルまたはディレクトリが削除された時にイベントが発生します。
Renamedファイルまたはディレクトリの名前が変更された時にイベントが発生します。
Changedサイズや属性、最終更新日時、最終アクセス日時などが変更された時にイベントが発生します。
Errorインスタンスが変更の監視を続けられない場合、または内部バッファー オーバーフローの場合に発生します。

サンプル

デスクトップ上にあるテキストファイルが更新(上書き保存)されたら、そのファイルを強制終了させて”sample write”という文字列を末尾に書くサンプルを作成します。

サンプルはこちら。

public partial class MainWindow : Window
{
    //FileSystemWatcherのオブジェクト名を設定
    System.IO.FileSystemWatcher watcher;
    public MainWindow()
    {
        InitializeComponent();

        Loaded += (s, e) =>
        {
            //監視するディレクトリの指定
            var directory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);

            //監視するディレクトリとファイルの種類を指定して初期化
            watcher = new System.IO.FileSystemWatcher(directory, "*.txt");

            //監視するフィールドの設定
            watcher.NotifyFilter =
                (NotifyFilters.Attributes
                | NotifyFilters.LastAccess
                | NotifyFilters.LastWrite
                | NotifyFilters.FileName
                | NotifyFilters.DirectoryName);

            //サブディレクトリは監視しない
            watcher.IncludeSubdirectories = false;

            //監視を開始する
            watcher.EnableRaisingEvents = true;
            Debug.WriteLine("Start!");

            //イベント設定
            watcher.Changed += new System.IO.FileSystemEventHandler(watcher_Changed);
            watcher.Created += new System.IO.FileSystemEventHandler(watcher_Changed);
            watcher.Deleted += new System.IO.FileSystemEventHandler(watcher_Changed);
            watcher.Renamed += new System.IO.RenamedEventHandler(watcher_Renamed);
        };
    }

    private void watcher_Created(object sender, FileSystemEventArgs e)
    {
        Debug.WriteLine("Created");
    }

    private DateTime lastWriteTimeSave = DateTime.Now;
    private void watcher_Changed(object sender, FileSystemEventArgs e)
    {
        Debug.WriteLine("Changed");

        //初回のみイベントを実行
        var file = new FileInfo(e.FullPath);
        if (file.LastWriteTime.Subtract(lastWriteTimeSave) < new TimeSpan(0, 0, 0, 1)) return;

        //メモ帳のプロセスをプロセスを強制的に終了させる
        Process[] ps = Process.GetProcessesByName("notepad");
        foreach (Process p in ps) p.Kill();

        //イベントを無効にする
        watcher.Changed -= watcher_Changed;

        //イベント発生元のファイルパスを取得しファイルを開く
        using (StreamWriter writer = new StreamWriter(e.FullPath, true, Encoding.GetEncoding("Shift_JIS")))
        {
            //文字を書き込む
            writer.WriteLine("sample write");
        };

        //更新日時を更新
        lastWriteTimeSave = file.LastWriteTime;

        //イベントを無効にする
        watcher.Changed += watcher_Changed;
    }

    private void watcher_Deleted(object sender, FileSystemEventArgs e)
    {
        Debug.WriteLine("Deleted");
    }

    private void watcher_Renamed(object sender, RenamedEventArgs e)
    {
        Debug.WriteLine("Renamed");
    }
}

インスタンスの初期化

記述量をちょっと減らすためにFileSystemWatcherのインスタンスを生成する際、監視するディレクトリとファイルの種類を指定しています。こうすることでプロパティでPathとFilterの設定が完了します。ここでは、テキストファイルのみ監視するので、第2引数に”*.txt”を指定します。

FileSystemWatcherの初期設定

Loadイベントで監視するNotifyFilterの設定、イベントの設定などを行います。この設定は用途にあわせて設定をし直す必要があります。

Changedイベント

このChangedイベントですが、複数回に分けて発生する場合があるので、使用する時は注意が必要です。外部のアプリケーションを使っている時に発生する場合が多く、ファイルを作成または変更する際に何度かに分けてアクセスして処理を行う場合があるからです。こうなるとこちらが意図していない不具合が発生してしまいます。

この問題の解決策として、連続でChangedイベントが発生した場合に初回のイベントのみ処理をするようにフィルターをかけます。ファイルの最終更新日時から1秒間未満に続けてイベントが発生した場合はイベントの処理をしないようにしています。

private DateTime lastWriteTimeSave = DateTime.Now;
private void watcher_Changed(object sender, FileSystemEventArgs e)
{
    Debug.WriteLine("Changed");

    //初回のみイベントを実行
    var file = new FileInfo(e.FullPath);
    if (file.LastWriteTime.Subtract(lastWriteTimeSave) < new TimeSpan(0, 0, 0, 1)) return;
}

上記のフィルターを通過した場合は、開いているメモ帳を全て強制的にプロセスを閉じます。メモ帳が開いたままだとテキストファイルへの書き込みが失敗するからです。

そのあとファイルへ”sample write”を書き込みします。

これで完成です。ファイルを更新(上書き保存)する度に指定した文字が末尾に記載される様になりました。これを応用すれば、どこかでうまいこと使えそうな使えなさそうそんな感じがします。

まとめ

今回は、FileSystemWatcherクラスの使い方と注意点について説明をしました。特にChangedイベントが複数回発生する場合の対処方法について記載されている記事が少ないように感じたので、参考になればと思います。

以上、最後まで読んで頂きありがとうございました。

プログラミングを学習したいなら…

プログラミングスキルを身に付けるなら、プログラミングを効率良く学べる
プログラミングスクール」がオススメです。

特にこんな方にオススメ!!
これからエンジニアを目指したい
プログラミングの専門性を高めたい
プログラミングを学んで副業をしたい
エンジニアに転職して年収をアップさせたい

プログラミングを触ったことがない未経験からでも、プログラミングスクールで学習すれば、エンジニアへ就職・転職することも可能です。

あなたの「行動力」と「やる気」で、あなたの人生を大きく変えるチャンスになることでしょう。

プログラミングスクールに興味がある方は是非チェックしてみてください。

> プログラミングを学ぶ <

COMMENT

メールアドレスが公開されることはありません。

CAPTCHA