FileSystemWatcherExplorer をGitHubに公開した。
dotNetの FileSystemWatcherクラスは、ファイル システムの変更通知を待機し、ディレクトリまたはディレクトリ内のファイルが変更されたときにイベントを発生させます。
作成したアプリケーションで時々指定ディレクトリにファイルがおかれたことを検出できない問題が時々発生しているので挙動確認用アプリケーションを作成した。
View
対策ソフト例
OnOnceCreate()が1度だけ実行するようにした。状況に合わせ修正のする必要がある。
ファイルに対すイベント発生後1秒程度後何も無ければOnOnceCreate()を実行する。
【key】
System.Threading.Timer クラス
System.Collections.Generic.Dictionary クラス
lock(...){...} のしくみ
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace SampleFileSystemWatcher.ViewModels
{
public class MainWindowViewModel
{
#region プロパティ 非公開
private FileSystemWatcher _fileSystemWatcher;
private string _path = @"D:\AA\BB\CC";
private string _filter = "*";
private Dictionary<string, DateTime> _dic;
private System.Threading.Timer _timer1;
private object _lockObject1 = new object(); // _dicの排他。
private int _stabilitytime = 1000; // 安定時間 [ms]
#endregion
public string WindowTitle { get; } = "SampleFileSystemWatcher";
// コンストラクタ
public MainWindowViewModel()
{
_fileSystemWatcher = new FileSystemWatcher(_path, _filter);
_fileSystemWatcher.NotifyFilter = NotifyFilters.FileName;
_fileSystemWatcher.Created += Watcher_OnCreated;
_fileSystemWatcher.Deleted += Watcher_OnDeleted;
_fileSystemWatcher.Changed += Watcher_OnChanged;
_fileSystemWatcher.Renamed += Watcher_OnRenamed;
_fileSystemWatcher.Error += Watcher_OnError;
_fileSystemWatcher.EnableRaisingEvents = true; // 開始
_dic = new Dictionary<string, DateTime>();
_timer1 = new System.Threading.Timer(timer1_callBack,_dic, _stabilitytime, Timeout.Infinite);
}
/// <summary>
/// 作成された。
/// 辞書にファイルのパスと時刻を追加する。
/// 辞書にファイルのパスが既に登録されているなら時刻を更新する。
/// note) timer1_callBack()が時間経過を確認
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Watcher_OnCreated(object sender, FileSystemEventArgs e)
{
DateTime now = DateTime.Now;
lock (_lockObject1)
{
if (_dic.ContainsKey(e.FullPath))
{
_dic[e.FullPath] = now;
}
else
{
_dic.Add(e.FullPath, now);
}
}
}
/// <summary>
/// タイマーハンドラー。
/// 辞書に登録されたファイルパスと時刻で、指定時間以上経過していたらOnOnceCreated()を呼び出す。
/// </summary>
/// <param name="state"></param>
private void timer1_callBack(object state)
{
// Debug.WriteLine($" ThreadId = {System.Threading.Thread.CurrentThread.ManagedThreadId}");
Dictionary<string, DateTime> dic = (Dictionary<string, DateTime>)state;
DateTime now = DateTime.Now;
Collection<string> keys = new Collection<string>(); // 最後に削除するkeyを保持する。
lock (_lockObject1)
{
foreach (var item in dic)
{
if ((now - item.Value) > TimeSpan.FromMilliseconds(_stabilitytime))
{
string fullPath = item.Key;
string name = Path.GetFileName(fullPath);
string directory = Path.GetDirectoryName(fullPath);
keys.Add(fullPath);
FileSystemEventArgs args = new FileSystemEventArgs(WatcherChangeTypes.Created, directory, name);
OnOnceCreated(null, args);
}
}
foreach (var key in keys)
{
dic.Remove(key);
}
}
_timer1.Change(_stabilitytime, Timeout.Infinite);
}
/// <summary>
/// 1どだけ実行
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnOnceCreated(object sender, FileSystemEventArgs e)
{
string falseMessage;
bool isFileExistsAndUnlocked = IsFileExistsAndUnlocked(e.FullPath , out falseMessage);
StringBuilder sb = new StringBuilder();
sb.AppendLine($"OnOnceCreated {e.ChangeType}");
Debug.WriteLine(sb);
}
private void Watcher_OnDeleted(object sender, FileSystemEventArgs e)
{
}
private void Watcher_OnChanged(object sender, FileSystemEventArgs e)
{
}
private void Watcher_OnRenamed(object sender, RenamedEventArgs e)
{
}
private void Watcher_OnError(object sender, ErrorEventArgs e)
{
}
// ファイルが存在して非ロックか。
private static bool IsFileExistsAndUnlocked(string path, out string falseMessage)
{
falseMessage = string.Empty;
FileStream stream = null;
if (!File.Exists(path))
{
falseMessage = "File.Exiets() is false.";
return false;
}
try
{
stream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
}
catch (DirectoryNotFoundException e)
{
falseMessage = e.Message;
return false;
}
catch (FileNotFoundException e)
{
falseMessage = e.Message;
return false;
}
catch (IOException e)
{
falseMessage = e.Message;
return false;
}
catch (Exception e)
{
falseMessage = e.Message;
return false;
}
finally
{
if (stream != null)
{
stream.Close();
}
}
return true;
}
}
}