中堅プログラマーの備忘録

忘れっぽくなってきたので備忘録として・・・

【C#.net】FileSystemWatcherの挙動について検証する

1.概要

Windowsで特定のディレクトリを監視して、操作に対するイベントを補足するには
【FileSystemWatcherクラス】を使うのが便利です。

【変更】、【作成】、【削除】、【名前の変更】に対して
それぞれイベントを補足できますが
操作によって挙動が異なるようです。

ここを理解していないと、アプリケーションがエラーを起こしたり
期待する動作にならないことがありますので
今回はこれについて調べてみようかと思います。


2.確認用ソース

動作確認用に下記のサイトのソースを参考にさせて頂きました。
dobon.net



これを少し改造したのが下記のソースになります。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FileSystemWatcherTest
{
    class Program
    {
        private static System.IO.FileSystemWatcher watcher = null;
        static void Main(string[] args)
        {
            if (watcher != null) return;

            watcher = new System.IO.FileSystemWatcher();
            //監視するディレクトリを指定
            watcher.Path = @"C:\work\test";
            //最終アクセス日時、最終更新日時、ファイル、フォルダ名の変更を監視する
            watcher.NotifyFilter =
                (System.IO.NotifyFilters.LastAccess
                | System.IO.NotifyFilters.LastWrite
                | System.IO.NotifyFilters.FileName
                | System.IO.NotifyFilters.DirectoryName);
            //すべてのファイルを監視
            watcher.Filter = "";

            //イベントハンドラの追加
            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);

            //監視を開始する
            watcher.EnableRaisingEvents = true;
            Console.WriteLine("Wacher Start.");
            Console.ReadLine();
        }


        private static void watcher_Changed(System.Object source, System.IO.FileSystemEventArgs e)
        {
            switch (e.ChangeType)
            {
                case System.IO.WatcherChangeTypes.Changed:
                    Console.WriteLine(e.FullPath + ":Changed");
                    break;
                case System.IO.WatcherChangeTypes.Created:
                    Console.WriteLine(e.FullPath + ":Created");

                    bool fileLocked = IsFileLocked(e.FullPath);
                    if (fileLocked)
                    {
                        Console.WriteLine(e.FullPath + ":Locked");
                    }
                    else
                    {
                        Console.WriteLine(e.FullPath + ":Unlocked");
                    }
                    break;
                case System.IO.WatcherChangeTypes.Deleted:
                    Console.WriteLine(e.FullPath + ":Deleted");
                    break;
            }
        }

        private static void watcher_Renamed(System.Object source, System.IO.RenamedEventArgs e)
        {
            Console.WriteLine(e.FullPath + ":Renamed");
        }

        private static 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;
        }
    }
}

やりたいことは
【C:\work\test】
を監視して、ファイルが作成されたタイミングでファイルにアクセスし
Lockされているかどうか(ファイルの使用中)の確認をしたいと思います。


3.確認方法

下記の3パターンのファイル作成で検証してみます。
①ドラッグアンドドロップ
②【Ctrl + v】でのコピー
③FTPでのファイル転送

使用するファイルは約7MBのmp4ファイルにしました。


4.検証結果

結果は下記のとおりとなりました。

①ドラッグアンドドロップ

Wacher Start.
C:\work\test\test.mp4:Created
C:\work\test\test.mp4:Unlocked

即時にファイルアクセスが可能でした。

②【Ctrl + v】でのコピー

Wacher Start.
C:\work\test\test.mp4:Created
C:\work\test\test.mp4:Locked
C:\work\test\test.mp4:Changed
C:\work\test\test.mp4:Changed

Createdイベントが発生した直後はアクセスが不可で
更にCreatedイベント後にChangedイベントが2回発生しています。

③FTPでのファイル転送

Wacher Start.
C:\work\test\test.mp4:Created
C:\work\test\test.mp4:Locked
C:\work\test\test.mp4:Changed

Createdイベントが発生した直後はアクセスが不可で
更にCreatedイベント後にChangedイベントが1回発生しています。

5.考察

検証結果から、すぐにファイルアクセスするのはやめておいた方がよさそうです。
Createdでイベントを補足し、
別ルーティンで処理がベストかもしれません。


Createdイベント発生後、Changedイベントが発生するのは
まずはファイルを作成してから中身を書き込むという処理が
走っているからだと推測しています。