16

次のような FileSystemWatcher を使用してフォルダーを監視しています。

watcher = new FileSystemWatcher(folder);
watcher.NotifyFilter = NotifyFilters.Size;
watcher.Changed += changedCallback;

そのフォルダーのメモ帳で新しいファイルを開いて保存すると、通知が表示されます。書き続けて保存すると、通知が届きます。ファイルを保存して閉じると、通知が表示されます。まさに私が欲しかったもの。

ただし、そのフォルダーにファイルを作成し、その共有モードを FileShare.Read に設定して書き込みを行うと、ファイルが閉じられるまで通知が届かないことがわかりました。もう 1 つの回避策は、(メモ帳などで) ファイルを開くことです。これにより、ファイルの状態が明らかに更新され、監視アプリケーションが通知を受け取ります。さらに別の回避策は、Windows エクスプローラーで実行できる更新です。これにより、ファイルの状態が更新されます。

興味深いことに、変更中に Windows エクスプローラーを見ると、次のことがわかります。

  1. ファイルが読み取りと書き込みのために共有されている場合、ファイルを保存するとすぐに、そのサイズが Windows エクスプローラーですぐに更新されます。
  2. ファイルが読み取り専用で共有されている場合、ウィンドウを手動で更新しない限り、そのサイズは Windows エクスプローラーですぐには更新されません。

したがって、私の監視アプリケーションは、Windows エクスプローラーと同じ動作を共有しているようです。フォルダー内のファイルをスキャンするだけのスレッドを実行することを考えていましたが、この場合、もっと洗練された方法があるかどうか疑問に思っています。

ところで、私は Win7 を使用していますが、この問題が他の Windows バージョンでも発生するかどうかはわかりません。

ありがとう!

編集: C++ で ReadDirectoryChanges を使用すると、まったく同じ結果が得られました。先ほど話したスレッドの実装も役に立ちませんでした。Windows エクスプローラーで F5 キーを押すと、変更が報告されるため、実際に何をしているのか気になります。

4

6 に答える 6

12

この問題の解決策は、ファイルを開くのではなく、実際にファイルから読み取ることです。1 バイトでも読み取るだけで十分であり、Windows のキャッシュ メカニズムによってファイルの内容がディスクに書き込まれるため、それらを読み取ることができます。

私は最終的に、すべてのファイルを調べ、それらを開いて、それらから 1 バイトを読み取るスレッドを実装しました。これにより、それらが変更され、FileSystemWatcher オブジェクトでイベントがトリガーされました。

Windows エクスプローラーの F5 キーも同様に機能する理由は、Windows が拡張コンテンツ (サムネイルなど) を表示するために実際にファイルのコンテンツを読み取るためです。ファイルが読み取られると、キャッシュは最初にディスクに書き込み、FSW でイベントをトリガーします。

于 2010-08-29T12:06:03.923 に答える
4

はい、ExplorerはFileSystemWatcherが使用するのとまったく同じAPIを使用します。ご存知のとおり、ReadDirectoryChangesW()が1つだけあります。

あなたが見つけたものは、Win7がファイルのディレクトリエントリを更新するために必要となるディスク書き込みを最適化していることを強く示唆しています。ファイルが閉じられる最後の可能な瞬間までそれを遅らせる。この観察結果と、ユーザーがRTMバージョンのWin7で発見した重大なバグとの間には興味深い相関関係があります。更新がまったく行われない場合があります。バグはランダムに発生しますが、まれにしか発生しません。自分のマシンでこれを1回だけ見たことがあります。とにかく故意に。

詳細はこのスレッドにあります(非常に遅いサーバーに注意してください)。これは、すべてのWin7更新プログラムが適用された状態で、今日でも失敗します。

さて、興味深い一口ですが、あなたの質問に実際には密接に関係していません。OSの動作に対応するために、コードを変更する必要があります。

于 2010-08-22T15:23:08.310 に答える
4

Windows サービスの作成中に、この同じ FileSystemWatcher の問題に遭遇しました。このサービスは .NET で作成され、FileSystemWatcher を使用して、サード パーティ製品によって生成されたログ ファイルを監視していました。テスト中に、強制的にログ エントリが書き込まれることを知っていたこのサード パーティ製品内でアクションを実行しましたが、対象のログ ファイルをメモ帳で開くか、Windows エクスプローラーでビューを更新するまで、サービスのブレークポイントが起動しませんでした。

私の解決策は、FileSystemWatcher を作成すると同時に FileInfo インスタンス (これを fileInfoInstance と呼びます) を作成することでした。FileSystemWatcher を開始または停止するたびに、コールバックが N ミリ秒ごとに fileInfoInstance.Refresh() を呼び出す System.Threading.Timer も開始または停止します。fileInfoInstance.Refresh() がバッファ/書き込みキャッシュをフラッシュし、FileSystemWatcher イベントが Explorer 内で F5 キーを押すのと同じ方法で発生できるように見えます。

興味深いことに (そして悲しいことに) fileInfoInstance.Directory.Refresh() では同じ結果が得られませんでした。監視しているファイルごとに FileInfo インスタンスが必要であり、タイマーコールバックは「ティック」ごとにすべてを更新する必要があります...

ハッピーコーディング。

ブライアン

于 2010-11-10T15:35:21.490 に答える
2

ファイルデータはキャッシュされており、実際には書き込まれていないようです。ファイルに何かを書き込むと、データは最初に特定のファイルシステムIRP(ドライバー要求)を使用してキャッシュに配置されます。これで、データが実際にディスクに書き込まれると、別のIRPが送信されます。FileSystemWatcherが2番目のIRPタイプのみをキャッチする可能性があります(これは推測です)。

解決策(可能な場合)は、書き込んでいるファイルに対してFlushを呼び出すことです。もちろん、他のアプリケーションによって行われた変更を追跡している場合、事態はさらに複雑になる可能性があります。

于 2010-08-22T09:56:31.773 に答える
1

実際、すべてのファイルを開いてそれらから 1 バイトを読み取るスレッドが必要です。私のプログラムは、XP ではなく Windows 7 で実行した後、動作を停止しました。次のコードを使用しました。

private void SingleByteReadThread(object notUsed)
    {
       while (true)
      {
         foreach (FileInfo fi in new DirectoryEnumerator(folderPath))
                  {
                      using (FileStream fs = new FileStream(fi.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                          fs.ReadByte();    
                  }

          Thread.Sleep(TimeSpan.FromSeconds(2));
      }
  }

DirectoryEnumerator は自分のクラスです

于 2010-11-21T00:31:19.290 に答える
0

ファイルの変更を書き込むには、FileStream Flush() メソッドを呼び出す必要があります。

于 2013-12-17T10:25:48.507 に答える