Windows で、リアルタイムでファイル変更情報を取得してみる

Yossy
2023-05-15
2023-05-15

ウェブサイトで利用するファイルの整理を行うとき、ファイルの移動や削除の履歴をとる必要がありました。結構量があるので、手動で取得は大変だなぁ・・・ということで、自動取得できる方法を考えてみたところ、.NET ライブラリの、FileSystemWatcher クラスを利用するとよさそうだったので試してみました。このクラスを使うと、ディレクトリやファイルに変更があった場合に、イベントを発生させてくれますので、イベント内にテキストファイルにログを残すコードを書くだけで今回の要望には応えられました。
コード自体は、下記URLに掲載されているサンプルコードに、ログ出力の機能を書き込むだけでよかったので、以下、作業時の記録を残しておきたいと思います。

https://learn.microsoft.com/ja-jp/dotnet/api/system.io.filesystemwatcher?view=net-7.0

まず、コードの大きな流れとしては、

(1) FileSystemWatcher クラスを New();
(2) NotifiFilter プロパティで、どのような変化に反応するかを指定
(3) 変化があったときに反応するイベント(例えば、Created) などを登録しておく。
(4) Path プロパティに検出対象を指定
(5) IncludeSubdirectories = true で、サブディレクトリ以下も検出対象になります。

(6) EnableRaisingEvents = true で、検出が開始されます。

という感じで、比較的簡単に使えます。ただ、利用にあたり、少々気にしておかないといけない点があったので、以下記録です。

(1) 検出対象が広いと、イベント発生が多くなりすぎて問題が起こることがある

このこと自体は問題はないのですが、イベントが大量に発生するので、イベントに書いた処理が重いと、いろいろ問題が起こります。
なので、イベント発生>キューに登録(イベントの処理自体は高速に終わらせる)、さらに別スレッドを立ち上げて、順次キューを処理するというような処理にしたほうが良いケースもありそうです。
Filter プロパティで拡張子で通知を制限できたのですが、複数の種類の拡張子を指定することはできないようでした。この場合は、複数の FileSystemWatcher クラスを立ち上げるのがよさそうです。
ちなみに、ログファイルの出力先が FileSystemWatcher の検出対象に含まれていると、書き込むたびにイベントが発生するので、コードから操作するファイルなどは、記録除外するなどの処理が必要になるケースがあるかもしれません。

(2) アクセス権限のないファイルの変化もイベントの対象になる。

例えば、テキストファイルにログを記録する前に、ファイル情報(ファイルサイズやファイル日付)を取得するようなコードを書いてると例外(エラー)発生しまくるので、例外処理を、かなり細かく書いておいたほうが幸せそうです。

(3) 取りこぼしがある

重い処理が走っていたり、処理するファイル数が多いと、取りこぼしがあり100%の変化を記録できない(イベント発生しない)ことがあります。メインスレッドに処理を渡さないような、重い処理を走らせる場合は、適時、Application.DoEvents を利用するなどで、FileSystemWatch クラスが動けるようにするようにして、取りこぼしを減らすことはできました。それでも、100%は難しいようです。

https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.forms.application.doevents?view=windowsdesktop-7.0

ちょっとクセ?のある FileSystemWatch クラスでしたが、非常に便利なクラスでした。