- PR -

FileSystemWatcherのChangedイベントが2回発生する

1
投稿者投稿内容
hide
会議室デビュー日: 2004/04/27
投稿数: 7
投稿日時: 2004-04-27 21:12
はじめて投稿させていただきます。
よろしくお願い致します。

早速ですが、
「System.IO.FileSystemWatcher」を使用して、
ある決まった(固定)ファイルの(内容)変更を監視したいのですが、
そのある決まったファイルに対して保存を行うと、
Changedイベントが必ず2回発生してしまうのです。
(Changedイベントを抜けた後に、
又Changedイベントの頭から処理が開始されてしまいます。)

Framework1.0,1.1共に起こります。
仕様なのでしょうが、なぜ2回発生するかの根本的情報がなかなか転がっておりません。

解決策と考えておりますのは、メンバ変数としてフラグを用意し、
1回目の起動と、2回目の起動をコード上で判断する。
といったことは思い浮かぶのですが、
なぜその様なコード上での考慮をしなくてはならないのか!と、
やるせなくなってしまいます。(おそらくコードが間違っているのでしょうが。)

MSDNの「System.IO.FileSystemWatcher」よりサンプルを拝見し、
動かしてみたのですが、やはり2回発生していました。

OSは2003Serverです。

以下コード記載します。
------------------------------------------------------
public class test
{
//ファイル監視メンバオブジェクト
private static FileSystemWatcher m_fileSystemWatcher = null;

//初期設定メソッド(まずこのメソッドが呼ばれます)
 public static Init()
{
//FileSystemWatcherの初期設定
m_fileSystemWatcher = new FileSystemWatcher();

m_fileSystemWatcher.Path = @"C:\Test";
m_fileSystemWatcher.Filter = "test.txt";
m_fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;
m_fileSystemWatcher.Changed += new FileSystemEventHandler( OnChanged ); //変更イベントハンドラの登録

//監視開始
m_fileSystemWatcher.EnableRaisingEvents = true;
}

//監視対象ファイルに対して、保存が行われたら処理が開始されるメソッド
private static void OnChanged( object source, FileSystemEventArgs e )
{
//ここで処理をおこないます。。。。が、ここの処理が2度発生してしまいます。
}
}
------------------------------------------------------

どなたか、心当たりがございましたら
なにとぞよろしくお願い致します。
なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 2004-04-27 22:01
引用:

hideさんの書き込み (2004-04-27 21:12) より:

そのある決まったファイルに対して保存を行うと、
Changedイベントが必ず2回発生してしまうのです。

仕様なのでしょうが、なぜ2回発生するかの根本的情報がなかなか転がっておりません。

なぜその様なコード上での考慮をしなくてはならないのか!と、
やるせなくなってしまいます。(おそらくコードが間違っているのでしょうが。)


実際に2回更新されてるから、とかそんなのはないでしょうか?
# 完全に1回でもそうなるのか、試してはいません。

なにでファイルを保存しているんでしょうか?
ファイルへの書き込みは、システム(OS)に対して完全に一度で行われているんでしょうか?

FileSystemWatcherが内部動作の詳細として、ファイルの更新をどのような粒度で拾っているのかはよく知りませんが、実際のファイルの更新が複数回に分かれて行われるというのは普通に起こりえることだと思います。

バッファリングしている場合なども、分かれている可能性がありますよね?
あるいは、その「保存」をしているのが何らかのソフトであれば、そちらが2回以上の更新を行う動作をしているのかもしれません。

単純に思いつくのは、実際に2回更新されてるからじゃないの?ってことです。
Jubei
ぬし
会議室デビュー日: 2002/03/02
投稿数: 830
お住まい・勤務地: 関西
投稿日時: 2004-04-27 22:49
諸農です。

引用:

hideさんの書き込み (2004-04-27 21:12) より:

ある決まった(固定)ファイルの(内容)変更を監視したいのですが、
そのある決まったファイルに対して保存を行うと、
Changedイベントが必ず2回発生してしまうのです。



考えられるのは、ファイル保存を行っているアプリケーションが、
書き込みを2回行っている可能性が高いということになります。

というのも、例えば、NotifyFilters.LastAccessのみで、テキスト
ファイルを監視した場合、メモ帳で書き込みをしても1回しか変更イ
ベントが発生しないのに対して、秀丸で書き込みを行うと2回の変更
イベントが発生します。
このことはSDKドキュメントのメモでほんの少し触れられていますね。


_________________
諸農和岳
Powered by Turbo Delphi & Microsoft Visual Studio 2005

十兵衛@わんくま同盟
http://blogs.wankuma.com/jubei/
hide
会議室デビュー日: 2004/04/27
投稿数: 7
投稿日時: 2004-04-28 09:57
なちゃさん。Jubeiさん。早急なお返事ありがとうございます。
大変参考になり、おかげで解決致しました。

解決方法の記述。の前に。

引用:
なちゃさんの書き込み (2004-04-27 22:01) より:

なにでファイルを保存しているんでしょうか?
ファイルへの書き込みは、システム(OS)に対して完全に一度で行われているんでしょうか?


保存を行っているエディタは「Notepad」を使用していました。
「エディタが原因かもしれない。。」と、そこまで頭が回りませんでした。すみません。

引用:
なちゃさんの書き込み (2004-04-27 22:01) より:

実際のファイルの更新が複数回に分かれて行われるというのは普通に起こりえることだと思います。



大変勉強になります。

以下解決方法です。

引用:
Jubeiの書き込み (2004-04-27 22:49) より:

というのも、例えば、NotifyFilters.LastAccessのみで、テキスト
ファイルを監視した場合、メモ帳で書き込みをしても1回しか変更イ
ベントが発生しないのに対して、秀丸で書き込みを行うと2回の変更
イベントが発生します。



「NotifyFilters.LastAccess(*1)」を使用し、「Notepad」で保存したところ、
見事に1回のみChangedイベントが発生致しました。
なので。やはりコードが間違っていたということになります。


 m_fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;
ではなく。
 m_fileSystemWatcher.NotifyFilter = NotifyFilters.LastAccess;

だったのですね。

(*1)
MSDNでNotifyFilters列挙体を調べ、
「LastAccess」 ・・・ ファイルまたはフォルダを最後に開いた日付。
「LastWrite」 ・・・ ファイルまたはフォルダへの最終書き込み日付。
と説明されていたので、「LastWrite」を使用してしまいました。

その他、色々試してみましたので、結果をお知らせいたします。
 1.エディタ「Notepad」、NotifyFilters「LastAccess」の場合。
   Changedイベント発生回数は(以下略)、1回。
 2.エディタ「Notepad」、NotifyFilters「LastWrite」の場合。2回。
 3.エディタ「秀丸」、NotifyFilters「LastAccess」の場合。2回。
 4.エディタ「秀丸」、NotifyFilters「LastWrite」の場合。3回!。

なちゃさん、Judeiさんのおっしゃる通り、
エディタによって保存方法が違ってくるのですね。

結論と致しまして、
フィルタは「LastAccess」を使用し、エディタは「Notepad」で!
ですね。

なちゃさん、Judeiさん、貴重なお時間を割かれてのお返事、
誠にありがとうございました。
そして大変勉強になりました。

またなにかありましたら、その際はよろしくお願い致します。
なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 2004-04-28 10:42
ちょこっと補足です。
引用:

hideさんの書き込み (2004-04-28 09:57) より:

「NotifyFilters.LastAccess(*1)」を使用し、「Notepad」で保存したところ、
見事に1回のみChangedイベントが発生致しました。
なので。やはりコードが間違っていたということになります。

 m_fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;
ではなく。
 m_fileSystemWatcher.NotifyFilter = NotifyFilters.LastAccess;


必ずしもコードが間違っているといえるわけでもありません。
※今回の場合、NotifyFilters.LastAccess を使用するのが必ずしも正しいとはいえない。
引用:

MSDNでNotifyFilters列挙体を調べ、
「LastAccess」 ・・・ ファイルまたはフォルダを最後に開いた日付。
「LastWrite」 ・・・ ファイルまたはフォルダへの最終書き込み日付。
と説明されていたので、「LastWrite」を使用してしまいました。


この判断自体は、けして間違ってはいないと思います。
引用:

結論と致しまして、
フィルタは「LastAccess」を使用し、エディタは「Notepad」で!


たとえば、
「LastAccess」 ・・・ ファイルまたはフォルダを最後に開いた日付。
とあるように、LastAccessを使用した場合、最後にファイルを開いたときということになります。
ここで、メモ帳の保存動作を分解してみると、単純に考えれば、
1.ファイルを開く
2.内容を書き込む(保存する)
3.ファイルを閉じる
のような流れになっているはずですね?

では、イベントが発生するのはどのタイミングでしょうか?
実験したわけではありませんが、普通に考えて、1.のタイミングになるはずです。ということは、処理のタイミング、ファイルの大きさ等に依存しますが、イベント発生時には、まだファイル書き込みは終了していない可能性があるということです。

確実にするには、いろいろ考えなければならないでしょう。単純には、ちょっとだけウェイトを入れて、まず大丈夫だろうという所であきらめるというのもありますが。

※他にも、タイミングによっては、メモ帳でファイルを開いた瞬間に反応してしまう可能性もありそうですね。

[ メッセージ編集済み 編集者: なちゃ 編集日時 2004-04-28 10:50 ]
hide
会議室デビュー日: 2004/04/27
投稿数: 7
投稿日時: 2004-04-28 12:24
なちゃさん。さらなるご考察ありがとうございます。

引用:
なちゃさんの書き込み (2004-04-28 10:42) より:

たとえば、
「LastAccess」 ・・・ ファイルまたはフォルダを最後に開いた日付。
とあるように、LastAccessを使用した場合、最後にファイルを開いたときということになります。
ここで、メモ帳の保存動作を分解してみると、単純に考えれば、
1.ファイルを開く
2.内容を書き込む(保存する)
3.ファイルを閉じる
のような流れになっているはずですね?

では、イベントが発生するのはどのタイミングでしょうか?



記述が足りませんでした。
なちゃさんのおっしゃられる通り、
「ファイルを開いた瞬間にイベントが発生するのでは?」
と思い、確認は取ってありました。
イベント発生タイミングは「2.内容を書き込む(保存する) 」です。

ですが、以下の引用より、気付かなかった点でしたので、再度検証を行いました。
引用:
なちゃさんの書き込み (2004-04-28 10:42) より:

ファイルの大きさ等に依存しますが



検証手順:
 @ファイルを新規作成
 A監視アプリの開始(イベント待ち)
 Bファイルを「Notepad」で開く
 C適当な文字列を書く(約60MB)
 D上書き保存する

検証結果:
 手順Dを実行後、アイコンが砂時計になり、待ち(保存中)が発生しました。
 アイコンが通常に戻り(保存完了)、その瞬間にイベントが発生しました。
 よって、確実に保存が完了した時点でイベント発生されますので、
 「サイズによる依存」は無いものと思われます。

ですが、検証中新たに発覚した現象を記述します。

現象:監視対象ファイルのサイズが「0バイト」の場合、
   「0バイト」のままで上書き保存をするとイベントが発生しない。

では、「NotifyFilters.LastAccess」ではなく
「NotifyFilters.LastWrite」ならば。。。
検証しました所、イベントが発生しました。(やはり2回発生しますが)

その他の検証項目としましては、(NotifyFilters.LastAccessでです。)
「Xバイト」(1バイト以上)のファイルを「0バイト」にして保存。・・・ イベント発生!
その「0バイト」ファイルをそのままの状態で保存。・・・ 未発生!

たしかに、「0バイト」から「0バイト」では、何も変わっていないので、
イベントが発生しないのはわかるのですが、
「1バイト」から「1バイト」も何も変えていないのに関らず、
イベントが発生します。(今までは後者の方法で検証していました。)

以上の検証ことより、以下の引用が自分自身混乱しております。
引用:
hideの書き込み (2004-04-28 09:57) より:

「LastAccess」 ・・・ ファイルまたはフォルダを最後に開いた日付。


NotifyFilters列挙体の参考資料:http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/cpref/html/frlrfsystemionotifyfiltersclasstopic.asp

「LastAccess」に関しましては、もう少し検証が必要な様ですね。
1

スキルアップ/キャリアアップ(JOB@IT)