ディレクトリやファイルの変更を検出できます。
public FileSystemWatcher ( string path, // 監視するディレクトリ string filter // 見張るファイルの種類 );FileSystemWatcher(String, String) - FileSystemWatcher Constructor (System.IO) | Microsoft Learn
pathは、標準またはUNC表記で指定します。
監視 (monitor) の対象とするとロックされるため、pathより上位のディレクトリは削除や名前の変更をできなくなります。しかしpathに対してはできます。
変更を検出できるのはpath内のファイルやディレクトリであって、path自体への変更は検出できません。pathを削除されても検出できませんが、名前を変更されたときは新しい名前に自動で再接続 (reattach) されるため監視は継続されます。
Filterプロパティに空文字列を設定すると"*.*"に変換されて設定されますが、引数のfilterにそれを設定するとそのまま空文字列に設定され、すべてのファイルが監視から除外されます。(.NET Framework 4.6で確認)
引数を省略するとpathは空文字列に設定されますが、そのまま監視を開始するとArgumentExceptionが投げられ失敗します。FileSystemWatcher - Source Browser
型 | プロパティ | 内容 | 既定値 |
---|---|---|---|
string | Path | ウォッチするディレクトリのパス | "" |
string | Filter | 監視するファイルを決定するために使用するフィルター文字列 | "*.*" |
NotifyFilters | NotifyFilter | ウォッチ (watch) する変更の種類 | LastWrite | FileName | DirectoryName |
bool | EnableRaisingEvents | コンポーネントが有効かどうか | false |
bool | IncludeSubdirectories | サブディレクトリを監視するかどうか | false |
ISynchronizeInvoke | SynchronizingObject | ディレクトリ変更の結果として発行されるイベント ハンドラー呼び出しをマーシャリングするために使用するオブジェクト | null |
int | InternalBufferSize | 内部バッファーのサイズ | 8192 |
既定値は空文字列であり、これにそれを設定することも可能ですが、そのままEnableRaisingEventsをtrueとして監視を開始すると、「パスの形式が無効です。」としてArgumentExceptionが投げられます。
ディレクトリ内で監視するファイルを指定できます。これには「*」と「?」のワイルドカードを指定できますが、"*.txt|*.doc"
のように複数のフィルタは指定できません。Remarks - FileSystemWatcher.Filter Property (System.IO) | Microsoft Learn
これはディレクトリに対しても適用されるため、たとえば*.txt
と設定していると、名前が.txtで終わるディレクトリしか監視の対象になりません。
監視する変更の種類を指定できます。これはNotifyFilters列挙型の組み合わせで指定します。
列挙子 | 数値 | 説明 |
---|---|---|
FileName | 1 | ファイルの名前
ファイル自体の名前だけではなく、作成やコピーによるファイルの名前の変更も対象となる |
DirectoryName | 2 | ディレクトリの名前
ディレクトリ自体の名前だけではなく、パスの一部の変更も対象となる |
Attributes | 4 | The attributes of the file or folder. |
Size | 8 | The size of the file or folder. |
LastWrite | 16 | The date the file or folder last had anything written to it. |
LastAccess | 32 | The date the file or folder was last opened. |
CreationTime | 64 | The time the file or folder was created. |
Security | 256 | The security settings of the file or folder. |
すべての値を指定するには、(NotifyFilters)0x17f
とすると簡単です。
Pathプロパティに監視するパスを指定し、このEnableRaisingEventsをtrueとすることで監視が開始されます。これはEnableRaisingEventsをfalseにするか、Dispose()によりオブジェクトが破棄されると停止します。
なおWaitForChanged()を呼び出すと、このプロパティがfalseであってもイベントが発生します。
Changedなどのイベントは、既定ではスレッドプールから呼ばれます。それを、このプロパティにControlクラスなどのISynchronizeInvokeを実装したクラスを設定することで、そのクラスと同一のスレッドから呼ばれるように変更できます。
このプロパティがnullのとき、ハンドラ内で投げられた例外を捕捉しないと通知なくアプリケーションが終了します。
内部バッファのバイト数を表します。既定は8192バイトで各イベントは最大16バイトのメモリーを使用するため、512ほどのイベントが同時に発生するとオーバーフローする恐れがあります。かといってバッファのサイズを増加させるのは高価なため、NotifyFilterとIncludeSubdirectoriesで対象を制限し、イベントの発生を抑制します。
型 | イベント | 発生タイミング |
---|---|---|
FileSystemEventHandler | Changed | ファイルまたはディレクトリの、サイズ、属性、更新日時、アクセス日時またはパーミッションが変更されたとき |
FileSystemEventHandler | Created | ファイルまたはディレクトリが作成されたとき。監視下のディレクトリへファイルがコピーや移動されたときも含む |
FileSystemEventHandler | Deleted | ファイルまたはディレクトリが削除されたとき。監視下のディレクトリからファイルが移動されたときも含む |
RenamedEventHandler | Renamed | ファイルまたはディレクトリの名前が変更されたとき。監視しているディレクトの名前が変更されたときを除く |
ErrorEventHandler | Error |
string path = @"C:\path"; // 監視するディレクトリ string filter = "*.txt"; // 監視するファイルの種類 FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(path, filter); fileSystemWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastWrite; fileSystemWatcher.IncludeSubdirectories = true; FileSystemEventHandler FileSystemEvent = delegate (object sender, FileSystemEventArgs e) { // FileInfo file = new FileInfo(e.FullPath); Console.WriteLine($"{e.ChangeType} : {e.FullPath}"); }; RenamedEventHandler RenamedEvent = delegate (object sender, RenamedEventArgs e) { Console.WriteLine($"{e.ChangeType} : {e.OldFullPath} -> {e.FullPath}"); }; ErrorEventHandler ErrorEvent = delegate (object sender, ErrorEventArgs e) { Exception exception = e.GetException(); Console.WriteLine($"{exception.HResult} {exception.Message}"); }; fileSystemWatcher.Changed += FileSystemEvent; fileSystemWatcher.Created += FileSystemEvent; fileSystemWatcher.Deleted += FileSystemEvent; fileSystemWatcher.Renamed += RenamedEvent; fileSystemWatcher.Error += ErrorEvent; fileSystemWatcher.EnableRaisingEvents = true;
ファイルシステムやアプリケーションによっては、複数のイベントが発生することがあります。Remarks - FileSystemWatcher.Changed Event (System.IO) | Microsoft Learn
エクスプローラで ファイルの新規作成 (NTFS) Created : C:\1.txt 同上 (FAT32、exFAT) Created : C:\1.txt Changed : C:\1.txt エクスプローラで 名前の変更 (NTFS、FAT32、exFAT共通) Renamed : C:\1.txt -> C:\2.txt エクスプローラで 大文字/小文字が異なるだけの名前の変更 (NTFS) ※FAT32やexFATではこのような変更はできない Deleted : C:\a.txt Renamed : C:\a.txt -> C:\A.txt エクスプローラで ファイルの削除 (共通) Deleted : C:\2.txt エクスプローラで 0バイトのファイルのコピー (共通) Created : C:\1 - コピー.txt Changed : C:\1 - コピー.txt エクスプローラで 1バイト以上のファイルのコピー (NTFS) Created : C:\3 - コピー.txt Changed : C:\3 - コピー.txt Changed : C:\3 - コピー.txt エクスプローラで 同名のファイルに上書きして ファイルのコピー (NTFS) Changed : C:\1.txt Changed : C:\1.txt Changed : C:\1.txt 同上 (FAT32、exFAT) Changed : C:\1.txt Changed : C:\1.txt
ディレクトリ内でファイルを操作すると、NTFSではそのディレクトリのChangedイベントも発生します。
エクスプローラで 名前の変更 (NTFS) Renamed : C:\DIR\1.txt -> C:\DIR\2.txt Changed : C:\DIR 同上 (FAT32、exFAT) Renamed : C:\DIR\1.txt -> C:\DIR\2.txt
move、copy、delコマンドで、名前の変更、削除、コピー、移動をした場合も、エクスプローラと同様にイベントが発生します。
エクスプローラで 監視外のディレクトリへファイルを移動 Deleted : C:\1.txt エクスプローラで 監視外から監視下のディレクトリへファイルを移動 Created : C:\1.txt エクスプローラで 監視下のディレクトリ内でファイルを移動 (NTFS) Deleted : C:\SRC\1.txt (移動元) Created : C:\DEST\1.txt (移動先) (ファイルの更新日時は 移動前の日時のまま) Changed : C:\DEST Changed : C:\SRC 同上 (FAT32、exFAT) Deleted : C:\SRC\1.txt (移動元) Created : C:\DEST\1.txt (移動先) (ファイルの更新日時は 移動前の日時のまま) エクスプローラで 監視下の2つのドライブ間でファイルを移動 (NTFS) Created : D:\1.txt (移動先) (ファイルの更新日時は 現在の日時) Changed : D:\1.txt Changed : D:\1.txt (ファイルの更新日時が 移動前の日時になる) Deleted : C:\1.txt (移動元) 同上 (FAT32、exFAT) Created : D:\1.txt (移動先) (ファイルの更新日時は 現在の日時) Changed : D:\1.txt (ファイルの更新日時が 移動前の日時になる) Deleted : C:\1.txt (移動元)
移動はDeletedとCreatedで通知されるため、これが移動によるものであると断定するには、2つのファイルが同一のものであることを確認しなければなりません。c# - Detecting moved files using FileSystemWatcher - Stack Overflow
エクスプローラで 監視下のディレクトリ内で 同名のファイルに上書きして ファイルを移動 (NTFS) Deleted : C:\DEST\1.txt (移動先) Deleted : C:\SRC\1.txt (移動元) Created : C:\DEST\1.txt Changed : C:\DEST Changed : C:\SRC 同上 (FAT32、exFAT) Deleted : C:\SRC\1.txt (移動元) Changed : C:\DEST\1.txt (移動先) エクスプローラで 監視下の2つのドライブ間で 同名のファイルに上書きして ファイルを移動 (NTFS) Changed : C:\1.txt (移動先) Changed : C:\1.txt Changed : C:\1.txt Deleted : D:\1.txt (移動元) 同上 (FAT32、exFAT) Changed : C:\1.txt (移動先) Changed : C:\1.txt Deleted : D:\1.txt (移動元)
// ファイル 1.txtを含むディレクトリ DIRをごみ箱に移動 (NTFS) Deleted : C:\DIR // ファイル 1.txtを含むディレクトリ DIRを完全に削除 (NTFS) Deleted : C:\DIR\1.txt Deleted : C:\DIR
// 2つのファイル 1.txt、2.txtを含むディレクトリ DIRをコピー (NTFS) Created : C:\DIR - コピー Changed : C:\DIR - コピー Created : C:\DIR - コピー\1.txt Changed : C:\DIR - コピー\1.txt Changed : C:\DIR - コピー\1.txt Created : C:\DIR - コピー\2.txt Changed : C:\DIR - コピー\2.txt Changed : C:\DIR - コピー\2.txt 同上 (FAT32、exFAT) Created : C:\DIR - コピー Changed : C:\DIR - コピー Created : C:\DIR - コピー\1.txt Changed : C:\DIR - コピー\1.txt Created : C:\DIR - コピー\2.txt Changed : C:\DIR - コピー\2.txt
// 2つのファイル 1.txt、2.txtを含むディレクトリ DIRを移動 (NTFS) Deleted : C:\SRC\DIR (移動元) Created : C:\DEST\DIR (移動先) Changed : C:\DEST Changed : C:\SRC 同上 (FAT32、exFAT) Deleted : C:\SRC\DIR (移動元) Created : C:\DEST\DIR (移動先) // 2つのファイル 1.txt、2.txtを含むディレクトリ DIRを監視下の2つのドライブ間で移動 (NTFS) Created : D:\DIR (移動先) Changed : D:\DIR Created : D:\DIR\1.txt Changed : D:\DIR\1.txt Changed : D:\DIR\1.txt Deleted : C:\DIR\1.txt (移動元) Changed : D:\DIR Created : D:\DIR\2.txt Changed : D:\DIR\2.txt Changed : D:\DIR\2.txt Deleted : C:\DIR\2.txt Changed : D:\DIR Deleted : C:\DIR 同上 (FAT32、exFAT) Created : D:\DIR (移動先) Changed : D:\DIR Created : D:\DIR\1.txt Changed : D:\DIR\1.txt Deleted : C:\DIR\1.txt (移動元) Created : D:\DIR\2.txt Changed : D:\DIR\2.txt Deleted : C:\DIR\2.txt Deleted : C:\DIR
DIR_A\DIR_B\1.txtのように配置された、上位のディレクトリを移動する場合を考えます。
// ディレクトリ DIR_Aを移動 (NTFS) Deleted : C:\SRC\DIR_A (移動元) Created : C:\DEST\DIR_A (移動先) Changed : C:\DEST Changed : C:\SRC // ディレクトリ DIR_Aを監視下の2つのドライブ間で移動 (NTFS) Created : D:\DIR_A (移動先) Changed : D:\DIR_A Created : D:\DIR_A\DIR_B Changed : D:\DIR_A\DIR_B Changed : D:\DIR_A Created : D:\DIR_A\DIR_B\1.txt Changed : D:\DIR_A\DIR_B\1.txt Changed : D:\DIR_A\DIR_B\1.txt Deleted : C:\DIR_A\DIR_B\1.txt (移動元) Changed : D:\DIR_A\DIR_B Deleted : C:\DIR_A\DIR_B Deleted : C:\DIR_A
ハンドラに渡されるFileSystemEventArgs.Nameプロパティの値は、監視しているディレクトリからの相対パスです。注釈 - FileSystemEventArgs.Name プロパティ (System.IO) | Microsoft Learn
ファイルを処理する方法によっては、Changedイベントは繰り返し発生することがあります。Remarks - FileSystemWatcher.Changed Event (System.IO) | Microsoft Learn
メモ帳で 名前を付けて保存 Created : C:\Save.txt (ファイルの更新日時は 1601/01/01 9:00:00) Deleted : C:\Save.txt Created : C:\Save.txt (ファイルの更新日時は 現在の日時) Changed : C:\Save.txt メモ帳で 上書き保存 Changed : C:\Save.txt Notepad++で 上書き保存 Changed : C:\Save.txt Changed : C:\Save.txt
c# - FileSystemWatcher Changed event is raised twice - Stack Overflow
イベントを発生させたのがファイルかディレクトリのいずれであるかをパスから判定するのは困難のため、FileSystemWatcherを2つ用意し、それぞれのNotifyFilterにFileNameとDirectoryNameを設定し別々に監視します。c# - .NET filesystemwatcher - was it a file or a directory? - Stack Overflow
内部バッファのオーバーフローなどにより、変更を監視できなくなったときに発生します。FileSystemWatcher.Error Event (System.IO) | Microsoft Learn
エラーの原因は、ハンドラのErrorEventArgs.GetException()からExceptionを取得することで判別でき、下表のような内容です。
HResult | Message |
---|---|
0x80004005 | "ファンクションが間違っています。" (Incorrect function.) |
"ネットワーク名が見つかりません。" | |
0x80131905 | "ディレクトリ *** で、一度に加えられた変更が多すぎます。" |
このイベントは監視できなくなったときに必ず発生するわけではなく、たとえばドライブが取り外されてもそれを検出できません。
FileSystemWatcherのイベントのデータが提供されます。
このクラスのコンストラクタは次のように定義されており、監視するディレクトリの絶対パスと検出されたファイルの相対パスを渡します。FileSystemEventArgs(WatcherChangeTypes, String, String) コンストラクター (System.IO) | Microsoft Learn
public FileSystemEventArgs(WatcherChangeTypes changeType, string directory, string name)
{
this.changeType = changeType;
this.name = name;
// Ensure that the directory name ends with a "\"
if (!directory.EndsWith("\\", StringComparison.Ordinal)) {
directory = directory + "\\";
}
this.fullPath = directory + name;
}
FileSystemEventArgs - FileSystemEventArgs.cs
型 | プロパティ | 内容 |
---|---|---|
string | FullPath | 影響を受けるファイルまたはディレクトリの絶対パス
(監視するディレクトリ + Name の文字列。監視するディレクトリのパスは、イベントの発生元であるFileSystemWatcherのPathプロパティから得られる) |
string | Name | 影響を受けるファイルまたはディレクトリの名前
(監視するディレクトリからの相対パスであり、ファイル名やディレクトリ名ではない) |
WatcherChangeTypes | ChangeType | 発生したディレクトリ イベントの種類 |
FileSystemEventArgsクラスを継承しており、それに「影響を受けるファイルまたはディレクトリの以前の名前」であるOldNameプロパティが追加されています。
public RenamedEventArgs(WatcherChangeTypes changeType, string directory, string name, string oldName)
: base(changeType, directory, name) {
// Ensure that the directory name ends with a "\"
if (!directory.EndsWith("\\", StringComparison.Ordinal)) {
directory = directory + "\\";
}
this.oldName = oldName;
this.oldFullPath = directory + oldName;
}
RenamedEventArgs - RenamedEventArgs.cs