お世話になります。
環境: C# (.netframework4.6.1) Windows10, 7
FileSystemWachterで、あるソフトから出力されるファイルの監視をしています。 (ソフトの仕様不明・ソース無)
ファイル変更イベントは発生しますが、ファイル変更イベント時に対象ファイルの読込を行うと次回のファイル変更イベントが発生しません。
ただし、次々回のファイル変更イベントは発生します。(必ず1回おきにファイル変更イベントが発生します)
対象ファイルの読込を行わないと正しく毎回ファイル変更イベントが発生します。
ファイル読込時のソースが外すと問題ありませんので影響していることは間違いないです。
ファイル読込時のソースをどのようにしたらよいでしょうか?
fileWatcher = new System.IO.FileSystemWatcher(); fileWatcher.Path = "c:\hoge"; fileWatcher.InternalBufferSize = 4096; fileWatcher.IncludeSubdirectories = false; fileWatcher.NotifyFilter = System.IO.NotifyFilters.Size; fileWatcher.Filter = "*.csv"; fileWatcher.Changed += watcher_Changed; fileWatcher.Created += watcher_Created; fileWatcher.Renamed += watcher_Renamed; isFileWatch = false; fileWatcher.EnableRaisingEvents = true; private void watcher_Changed(System.Object source, System.IO.FileSystemEventArgs e) { //デバッグログ出力 if (!isFileWatch) { isFileWatch = true; //↓ここから using (FileStream fs = new FileStream(e.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { using (TextReader sr = new StreamReader(fs, Encoding.GetEncoding("UTF-8"))) { String buff = ""; while ((buff = sr.ReadLine()) != null) { } sr.Close(); } fs.Close(); } //↑ここまでがあるとダメです。 isFileWatch = false; } }
ログ出力ソフトのテストプログラム
private StreamWriter sw; public Form1() { InitializeComponent(); sw = new StreamWriter(File.Open(@"C:\hoge\aaa.csv", FileMode.OpenOrCreate, FileAccess.Write,FileShare.Read),Encoding.Default); } private void button1_Click(object sender, EventArgs e) { sw.WriteLine(DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")); sw.Flush(); } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { sw.Close(); }
どの程度の間隔でファイルの更新が行われるのですか?
30秒~1分程度です。
ソフトの仕様は不明ですが、動作から判断しますと、
出力ファイルをオープンしたままで、日付が切り替わるとファイルが変わりますが、
その際にCloseされるようです。
ソースは ``` と ``` で囲ってください。インデントされて見やすくなるので。インデントされてないコードは質問者さん自身も読む気がしないのでは?
ありがとうございます。インデントを追加しました。
読んでる間に次の出力があったから飛ばされただけという気がします。
読み込みの開始、終了にログ出力して、確実に読み込み終了後に更新が行われているか確認してはどうでしょうか。
提示ソースで監視し、30秒ごとにファイルを更新するプログラムを作成して確認しましたが、イベントは正常に発生しています。
出力ファイルの内容を確認したのですが、次の読込までに出力がありませんでした。
FileStreamを変更イベントで毎回オープンするのではなく、予めオープンしておいて使い回す形にしたらファイル変更イベントが正しく動作しました。
変更を検出したら、そのファイルのコピーを作ってそっちを読んだらって案を出そうとしましたが
> 予めオープンしておいて使い回す形
でいけるならそれが良さそうですね。
>FileStreamを変更イベントで毎回オープンするのではなく、予めオープンし>ておいて使い回す形にしたらファイル変更イベントが正しく動作しました
申し訳ありません。
さらに検証を進めましたが、上記のやり方でもうまくいかないことがわかりました。
最初の if 文の直前でログをとってみてください。
記録するのは isFileWatch です。
これが true になることがあるなら、読み込み中に対象ファイルが変更されています。
なぜかこの可能性が検証の方法の記述もなく軽くスルーされましたが、私はこの可能性は否定できないと思います。
というより最も可能性が高いと思っています。
そうでなければどこかで例外が握りつぶされていそうな気がします。
ご回答ありがとうございます。
最初の if 文の直前でログを取りましたが、イベント発生していませんでした。
fileWatcher.Error += new ErrorEventHandler(watcher_Error);
を入れてみましたが、エラーは発生していないようです。
try catch無のミニマムコードで確認してみましたが、エラーは起きていないように思います。
「isFileWatch を設定しなければ意図通り動く」というのは正確ですか?
ここが正しくないなら前提が崩れます。
誤解を招くソースコードに問題で、申し訳ありませんでした。
コードを修正しました。
using (FileStream fs ~のブロックがあると正しく動作しません。
そこがメイン処理なので、それを取ると正しくどころか何もしなくなると思いますが。
とりあえず FileShare.ReadWrite は write が不要なので FileShare.Read に変えてみてください。
そしてブレークポイントを設定してステップ実行し、実際にどのように動いているのか確認してください。
一回おきというのもどうやってそれを確かめたのかよくわかりません。
本当に一回おきなのかというところも確かめ、方法を含めて書いてください。
FileShare.Readとすると、別のプロセスから書き込みできなくなる認識です。処理中にログ出力ソフトから書き込みできなくなってしまうと困るため、FileShare.ReadWriteとしました。
ソース内の「//デバッグログ出力」と記載した箇所でデバッグログを出力していますが、
ログ出力ソフト側でファイル出力があったタイミングが1回おきにデバッグログが出力されていました。
ここまで記載してみて、ログ出力ソフトが本当に正しく出力されているか確認していないことに気づきました。ログ出力ソフト側も合わせて調査してみます。
node-jsの「chokidar」を使用したソースですと思った通りのタイミングでファイル変更検知できました。
同様のことを別のWindowsアプリ「Terais」で行うと自分が作成したソースと同じタイミングでしか(1回おき)イベント発生しませんでした。
イベント発生しない場合に備えて、タイマーでファイルを定期監視することで補完する予定です。
当然です。書き込みオープンできてはいけません。オープンのタイミングを調べるために外します。
オープンは一日一回だけのはずです。
ありがとうございます。理解しました。
ご指摘通り、既にログ出力ソフト側はオープン済みのため、このソース以外で書き込みオープンするものはないはずですね。
明日、所要により試せないため、明後日に検証後に報告させていただきます。
結果はうまくいきませんでした。
ログ出力ソフトをエミュレートした仮想ログ出力ソフトを作成してみました。
こちらでテストするとそもそもFileSystemWatcherで変更イベントが起きませんでした。コード中のFlush()とした時点で変更がOS側に通知されるのかなと思いましたが…
StreamWriter.Flush()は恐らくキャッシュ上の書き込みデータをディスクに書き込むだけな為、FileSystemWatcherで監視すべき(あるいは対象の)
イベントが発生しないのでしょう。SysinternalsのProcessMonitorで当該ファイルのアクセス状況を確認してみると、なにか分かるかもしれません。https://docs.microsoft.com/ja-jp/sysinternals/downloads/procmon
ご回答ありがとうございます。
Flush()時点ではプロパティのファイルサイズは変更になっていましが、更新日時などは変更されていませんでした。
フィルターが「System.IO.NotifyFilters.Size」でもサイズ変更を検知するのがファイルクローズ後だとすると、書き込み中のFlush()でイベントが発生しなくても仕方がないです。
あなたの回答
tips
プレビュー