366

テキストファイルを探しているアプリケーションがあり、ファイルに変更が加えられている場合はOnChanged、イベントハンドラーを使用してイベントを処理しています。私は使用してNotifyFilters.LastWriteTimeいますが、それでもイベントは2回発生しています。これがコードです。

public void Initialize()
{
   FileSystemWatcher _fileWatcher = new FileSystemWatcher();
  _fileWatcher.Path = "C:\\Folder";
  _fileWatcher.NotifyFilter = NotifyFilters.LastWrite;
  _fileWatcher.Filter = "Version.txt";
  _fileWatcher.Changed += new FileSystemEventHandler(OnChanged);
  _fileWatcher.EnableRaisingEvents = true;
}

private void OnChanged(object source, FileSystemEventArgs e)
{
   .......
}

私の場合、OnChangedテキストファイルを変更してversion.txt保存すると、が2回呼び出されます。

4

41 に答える 41

291

残念ながら、これはFileSystemWatcherクラスのよく知られたバグ/機能です。これは、クラスのドキュメントからのものです。

特定の状況では、1 つの作成イベントが、コンポーネントによって処理される複数の Created イベントを生成することに気付く場合があります。たとえば、FileSystemWatcher コンポーネントを使用してディレクトリ内の新しいファイルの作成を監視し、メモ帳を使用してファイルを作成してテストすると、ファイルが 1 つしか作成されていない場合でも、2 つの Created イベントが生成されることがあります。これは、メモ帳が書き込みプロセス中に複数のファイル システム アクションを実行するためです。メモ帳は、ファイルのコンテンツを作成してからファイル属性を作成するバッチでディスクに書き込みます。他のアプリケーションも同じように動作する場合があります。FileSystemWatcher はオペレーティング システムのアクティビティを監視するため、これらのアプリケーションが起動するすべてのイベントが取得されます。

このテキストはCreatedイベントに関するものですが、同じことが他のファイル イベントにも当てはまります。一部のアプリケーションでは、プロパティを使用してこれを回避できる場合がありますNotifyFilterが、私の経験では、手動で重複フィルタリング (ハック) を行う必要がある場合もあります。

少し前に、いくつかのFileSystemWatcher のヒントが記載されたページにブックマークを付けました。あなたはそれをチェックしたいかもしれません。

于 2009-11-19T17:31:18.090 に答える
151

デリゲートで次の戦略を使用して、その問題を「修正」しました。

// fsw_ is the FileSystemWatcher instance used by my application.

private void OnDirectoryChanged(...)
{
   try
   {
      fsw_.EnableRaisingEvents = false;

      /* do my stuff once asynchronously */
   }

   finally
   {
      fsw_.EnableRaisingEvents = true;
   }
}
于 2010-05-06T11:53:42.667 に答える
112

からの重複OnChangedしたイベントは、問題のファイルのタイムスタンプをFileSystemWatcherチェックすることで検出および破棄できます。File.GetLastWriteTimeそのようです:

DateTime lastRead = DateTime.MinValue;

void OnChanged(object source, FileSystemEventArgs a)
{
    DateTime lastWriteTime = File.GetLastWriteTime(uri);
    if (lastWriteTime != lastRead)
    {
        doStuff();
        lastRead = lastWriteTime;
    }
    // else discard the (duplicated) OnChanged event
}
于 2010-06-15T06:24:14.027 に答える
25

イベントが2回発生するのを止めるのに役立った私の解決策は次のとおりです。

watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size;

ここでは、NotifyFilterファイル名とサイズのみでプロパティを設定しました。
watcher私のFileSystemWatcherのオブジェクトです。これが役立つことを願っています。

于 2011-11-21T08:11:32.367 に答える
9

My scenario is that I have a virtual machine with a Linux server in it. I am developing files on the Windows host. When I change something in a folder on the host I want all the changes to be uploaded, synced onto the virtual server via Ftp. This is how I do eliminate the duplicate change event when I write to a file ( which flags the folder containing the file to be modified as well ) :

private Hashtable fileWriteTime = new Hashtable();

private void fsw_sync_Changed(object source, FileSystemEventArgs e)
{
    string path = e.FullPath.ToString();
    string currentLastWriteTime = File.GetLastWriteTime( e.FullPath ).ToString();

    // if there is no path info stored yet
    // or stored path has different time of write then the one now is inspected
    if ( !fileWriteTime.ContainsKey(path) ||
         fileWriteTime[path].ToString() != currentLastWriteTime
    )
    {
        //then we do the main thing
        log( "A CHANGE has occured with " + path );

        //lastly we update the last write time in the hashtable
        fileWriteTime[path] = currentLastWriteTime;
    }
}

Mainly I create a hashtable to store file write time information. Then if the hashtable has the filepath that is modified and it's time value is the same as the currently notified file's change then I know it is the duplicate of the event and ignore it.

于 2010-08-13T07:34:38.710 に答える
9

これが私のアプローチです:

// Consider having a List<String> named _changedFiles

private void OnChanged(object source, FileSystemEventArgs e)
{
    lock (_changedFiles)
    {
        if (_changedFiles.Contains(e.FullPath))
        {
            return;
        }
        _changedFiles.Add(e.FullPath);
    }

    // do your stuff

    System.Timers.Timer timer = new Timer(1000) { AutoReset = false };
    timer.Elapsed += (timerElapsedSender, timerElapsedArgs) =>
    {
        lock (_changedFiles)
        {
            _changedFiles.Remove(e.FullPath);
        }
    };
   timer.Start();
}

これは、ファイルを添付ファイルとしてメールで送信していたプロジェクトでこの問題を解決するために使用したソリューションです。タイマー間隔を短くしても、2 回発生するイベントを簡単に回避できますが、私の場合は 1000 で問題ありませんでした。1 秒あたり 1 件以上のメッセージでメールボックスをあふれさせるよりも、いくつかの変更を逃した方がよかったからです。少なくとも、複数のファイルがまったく同時に変更された場合には問題なく動作します。

私が考えた別の解決策は、リストを、ファイルをそれぞれのMD5にマッピングする辞書に置き換えることです。これにより、エントリを削除する必要はなく、その値を更新する必要がないため、任意の間隔を選択する必要がなくなります。変更されていない場合はキャンセルしてください。ファイルが監視され、より多くのメモリを消費するにつれて辞書がメモリ内で成長するという欠点がありますが、監視されるファイルの量はFSWの内部バッファに依存することをどこかで読んだので、それほど重要ではないかもしれません. MD5 の計算時間がコードのパフォーマンスにどのように影響するかはわかりません。注意してください =\

于 2011-01-23T02:16:32.377 に答える
7

このコードで試してください:

class WatchPlotDirectory
{
    bool let = false;
    FileSystemWatcher watcher;
    string path = "C:/Users/jamie/OneDrive/Pictures/Screenshots";

    public WatchPlotDirectory()
    {
        watcher = new FileSystemWatcher();
        watcher.Path = path;
        watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
                               | NotifyFilters.FileName | NotifyFilters.DirectoryName;
        watcher.Filter = "*.*";
        watcher.Changed += new FileSystemEventHandler(OnChanged);
        watcher.Renamed += new RenamedEventHandler(OnRenamed);
        watcher.EnableRaisingEvents = true;
    }



    void OnChanged(object sender, FileSystemEventArgs e)
    {
        if (let==false) {
            string mgs = string.Format("File {0} | {1}",
                                       e.FullPath, e.ChangeType);
            Console.WriteLine("onchange: " + mgs);
            let = true;
        }

        else
        {
            let = false;
        }


    }

    void OnRenamed(object sender, RenamedEventArgs e)
    {
        string log = string.Format("{0} | Renamed from {1}",
                                   e.FullPath, e.OldName);
        Console.WriteLine("onrenamed: " + log);

    }

    public void setPath(string path)
    {
        this.path = path;
    }
}
于 2016-06-09T12:59:54.340 に答える
3

私は FileSystemWatcher を使用するのにかなりの時間を費やしましたが、ここでのアプローチのいくつかは機能しません。イベントを無効にするアプローチはとても気に入りましたが、残念ながら、1 つ以上のファイルがドロップされている場合は機能しません。そこで、次のアプローチを使用します。

private void EventCallback(object sender, FileSystemEventArgs e)
{
    var fileName = e.FullPath;

    if (!File.Exists(fileName))
    {
        // We've dealt with the file, this is just supressing further events.
        return;
    }

    // File exists, so move it to a working directory. 
    File.Move(fileName, [working directory]);

    // Kick-off whatever processing is required.
}
于 2015-10-08T21:24:21.010 に答える
3

考えられる「ハック」の 1 つは、たとえば、Reactive Extensions を使用してイベントを抑制することです。

var watcher = new FileSystemWatcher("./");

Observable.FromEventPattern<FileSystemEventArgs>(watcher, "Changed")
            .Throttle(new TimeSpan(500000))
            .Subscribe(HandleChangeEvent);

watcher.EnableRaisingEvents = true;

この場合、私のシステムでは 50 ミリ秒に調整していますが、これで十分でしたが、より高い値の方が安全なはずです。(そして、私が言ったように、それはまだ「ハック」です)。

于 2014-02-10T06:54:06.623 に答える
3

これがあなたが試すことができる新しい解決策です。私にとってはうまくいきます。変更されたイベントのイベント ハンドラーで、必要に応じてデザイナー出力メッセージからハンドラーをプログラムで削除し、プログラムでハンドラーを追加し直します。例:

public void fileSystemWatcher1_Changed( object sender, System.IO.FileSystemEventArgs e )
    {            
        fileSystemWatcher1.Changed -= new System.IO.FileSystemEventHandler( fileSystemWatcher1_Changed );
        MessageBox.Show( "File has been uploaded to destination", "Success!" );
        fileSystemWatcher1.Changed += new System.IO.FileSystemEventHandler( fileSystemWatcher1_Changed );
    }
于 2016-09-22T21:20:43.077 に答える
2

このコードは私のために働いた。

        private void OnChanged(object source, FileSystemEventArgs e)
    {

        string fullFilePath = e.FullPath.ToString();
        string fullURL = buildTheUrlFromStudyXML(fullFilePath);

        System.Diagnostics.Process.Start("iexplore", fullURL);

        Timer timer = new Timer();
        ((FileSystemWatcher)source).Changed -= new FileSystemEventHandler(OnChanged);
        timer.Interval = 1000;
        timer.Elapsed += new ElapsedEventHandler(t_Elapsed);
        timer.Start();
    }

    private void t_Elapsed(object sender, ElapsedEventArgs e)
    {
        ((Timer)sender).Stop();
        theWatcher.Changed += new FileSystemEventHandler(OnChanged);
    }
于 2016-09-14T11:59:20.380 に答える
2

主な理由は、最初のイベントの最終アクセス時間が現在の時間 (ファイルの書き込みまたは変更時間) だったことです。2 番目のイベントは、ファイルの元の最終アクセス時刻でした。コードの下で解決します。

        var lastRead = DateTime.MinValue;

        Watcher = new FileSystemWatcher(...)
        {
            NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite,
            Filter = "*.dll",
            IncludeSubdirectories = false,
        };
        Watcher.Changed += (senderObject, ea) =>
        {
            var now = DateTime.Now;
            var lastWriteTime = File.GetLastWriteTime(ea.FullPath);

            if (now == lastWriteTime)
            {
                return;
            }

            if (lastWriteTime != lastRead)
            {
                // do something...
                lastRead = lastWriteTime;
            }
        };

        Watcher.EnableRaisingEvents = true;
于 2015-05-31T12:39:33.457 に答える
2

ここに非常に迅速で簡単な回避策があります。それは私にとってはうまくいきます。イベントが1回または2回またはそれ以上トリガーされる場合でも、チェックしてください

private int fireCount = 0;
private void inputFileWatcher_Changed(object sender, FileSystemEventArgs e)
    {
       fireCount++;
       if (fireCount == 1)
        {
            MessageBox.Show("Fired only once!!");
            dowork();
        }
        else
        {
            fireCount = 0;
        }
    }
}
于 2016-03-08T13:54:26.560 に答える
1

私の場合、挿入が完了するとすぐに、他のアプリケーションによって挿入されたテキスト ファイルの最後の行を取得する必要があります。これが私の解決策です。最初のイベントが発生すると、ウォッチャーが他のイベントを発生させないようにし、タイマー TimeElapsedEvent を呼び出します。これは、ハンドル関数 OnChanged が呼び出されたときにテキスト ファイルのサイズが必要なためですが、そのときのサイズは実際のサイズではありません。挿入直前のファイルのサイズです。そのため、適切なファイルサイズで続行するまでしばらく待ちます。

private FileSystemWatcher watcher = new FileSystemWatcher();
...
watcher.Path = "E:\\data";
watcher.NotifyFilter = NotifyFilters.LastWrite ;
watcher.Filter = "data.txt";
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = true;

...

private void OnChanged(object source, FileSystemEventArgs e)
   {
    System.Timers.Timer t = new System.Timers.Timer();
    try
    {
        watcher.Changed -= new FileSystemEventHandler(OnChanged);
        watcher.EnableRaisingEvents = false;

        t.Interval = 500;
        t.Elapsed += (sender, args) => t_Elapsed(sender, e);
        t.Start();
    }
    catch(Exception ex) {
        ;
    }
}

private void t_Elapsed(object sender, FileSystemEventArgs e) 
   {
    ((System.Timers.Timer)sender).Stop();
       //.. Do you stuff HERE ..
     watcher.Changed += new FileSystemEventHandler(OnChanged);
     watcher.EnableRaisingEvents = true;
}
于 2019-01-31T17:03:57.007 に答える
1

書き込み用に開いてみて、成功した場合は、他のアプリケーションがファイルを処理したと見なすことができます。

private void OnChanged(object source, FileSystemEventArgs e)
{
    try
    {
        using (var fs = File.OpenWrite(e.FullPath))
        {
        }
        //do your stuff
    }
    catch (Exception)
    {
        //no write access, other app not done
    }
}

書き込み用に開くだけでは、変更されたイベントが発生しないようです。だから安全なはずです。

于 2009-12-14T20:37:56.857 に答える
0

ディレクトリ内のファイルを監視する方法を変更しました。FileSystemWatcher を使用する代わりに、別のスレッドで場所をポーリングし、ファイルの LastWriteTime を確認します。

DateTime lastWriteTime = File.GetLastWriteTime(someFilePath);

この情報を使用して、ファイル パスのインデックスと最新の書き込み時刻を保持することで、特定の場所で変更されたファイルや作成されたファイルを特定できます。これにより、FileSystemWatcher の奇妙さから解放されます。主な欠点は、LastWriteTime とファイルへの参照を格納するためのデータ構造が必要なことですが、信頼性が高く、実装が簡単です。

于 2009-11-19T17:43:38.843 に答える
0

2 番目のウォッチャーの発生をブロックする時間間隔をカスタマイズ可能で無効にし、ウォッチャーが存在する場合はブロックしないコード:

    namespace Watcher
    {
        class Static
        {
            public static DateTime lastDomain { get; set; }
            public static string lastDomainStr { get; set; }
        }
        public partial class Form1 : Form
       {
            int minMs = 20;//time for blocking in ms
            public Form1()
            {
                InitializeComponent();
                Static.lastDomain = new DateTime(1970, 1, 1, 0, 0, 0);
                Static.lastDomainStr = "";  
                Start();
            }
             private void Start()//Start watcher
             {
                //...
                domain.Changed += new FileSystemEventHandler(Domain);
                domain.EnableRaisingEvents = true;
                //...you second unblocked watchers
                second.Changed += new FileSystemEventHandler(Second);
                second.EnableRaisingEvents = true;
             }
             private void Domain(object source, FileSystemEventArgs e)
             {
                if (now.Subtract(Static.lastDomain).TotalMilliseconds < minMs && Static.lastDomainStr == e.FullPath)return;
                 //...you code here
                 /* if you need form access
                 this.Invoke(new MethodInvoker(() =>{ textBox1.Text = "...";}));
                 */
                 Static.lastDomain = DateTime.Now;
                 Static.lastDomainStr = e.FullPath;
             }
             private void Second(object source, FileSystemEventArgs e)
             {
                  //...Second rised
             }
       }
    }
于 2019-03-11T11:14:37.467 に答える
0

これを試して!

string temp="";

public void Initialize()
{
   FileSystemWatcher _fileWatcher = new FileSystemWatcher();
  _fileWatcher.Path = "C:\\Folder";
  _fileWatcher.NotifyFilter = NotifyFilters.LastWrite;
  _fileWatcher.Filter = "Version.txt";
  _fileWatcher.Changed += new FileSystemEventHandler(OnChanged);
  _fileWatcher.EnableRaisingEvents = true;
}

private void OnChanged(object source, FileSystemEventArgs e)
{
   .......
if(temp=="")
{
   //do thing you want.
   temp = e.name //name of text file.
}else if(temp !="" && temp != e.name)
{
   //do thing you want.
   temp = e.name //name of text file.
}else
{
  //second fire ignored.
}

}
于 2017-04-12T03:40:07.680 に答える
-2

OnChanged イベントに登録する場合は、OnChange イベントのみを監視する必要がある限り、変更する前に監視対象のファイルを削除することで機能する可能性があります。

于 2010-06-20T13:16:19.083 に答える
-6

さて、これがイベントを一度だけ発生させる私の解決策です:

FileSystemWatcheк watcher = new FileSystemWatcher();

//'path' - path to the file that has been modified.
watcher.Changed += (s, e) => FileChanged(path);

これがFileChangedの実装です

//count is our counter to triger when we can raise and when not.
private int count = 0;
private void FileChanged(string path)
{
   if (count % 2 == 0)
     {
       //code here
     }

     count ++;
}
于 2011-07-20T13:20:42.770 に答える
-6

1 つのグローバル変数を簡単に定義しますvar1 = true

Private Sub FileWatchman_Changed(ByVal sender As System.Object, ByVal e As System.IO.FileSystemEventArgs) Handles FileWatchman.Changed
   If var1 = true 
       your logic goes here
       var1 = false
   Else
       var1 = true 
   End If
End Sub
于 2012-03-22T11:28:14.540 に答える