我有一个应用程序,我正在寻找一个文本文件,如果对文件做了任何更改,我使用OnChanged事件处理程序来处理事件。我正在使用NotifyFilters。LastWriteTime,但是事件仍然被触发两次。这是代码。

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并保存它。


当前回答

我简单地添加了一个dupe检查如下:

 private void OnChanged(object source, FileSystemEventArgs e)
    {
        string sTabName = Path.GetFileNameWithoutExtension(e.Name);
        string sLastLine = ReadLastLine(e.FullPath);
        if(sLastLine != _dupeCheck)
        {
            TabPage tp = tcLogs.TabPages[sTabName];
            TextBox tbLog = (TextBox)tp.Controls[0] as TextBox;

            tbLog.Invoke(new Action(() => tbLog.AppendText(sLastLine + Environment.NewLine)));
            tbLog.Invoke(new Action(() => tbLog.SelectionStart = tbLog.Text.Length));
            tbLog.Invoke(new Action(() => tbLog.ScrollToCaret()));
            _dupeCheck = sLastLine;
        }
    }

    public static String ReadLastLine(string path)
    {
        return ReadLastLine(path, Encoding.Default, "\n");
    }

    public static String ReadLastLine(string path, Encoding encoding, string newline)
    {
        int charsize = encoding.GetByteCount("\n");
        byte[] buffer = encoding.GetBytes(newline);
        using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
            long endpos = stream.Length / charsize;
            for (long pos = charsize; pos < endpos; pos += charsize)
            {
                stream.Seek(-pos, SeekOrigin.End);
                stream.Read(buffer, 0, buffer.Length);
                if (encoding.GetString(buffer) == newline)
                {
                    buffer = new byte[stream.Length - stream.Position];
                    stream.Read(buffer, 0, buffer.Length);
                    return encoding.GetString(buffer);
                }
            }
        }
        return null;
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);

    private const int WM_VSCROLL = 0x115;
    private const int SB_BOTTOM = 7;

    /// <summary>
    /// Scrolls the vertical scroll bar of a multi-line text box to the bottom.
    /// </summary>
    /// <param name="tb">The text box to scroll</param>
    public static void ScrollToBottom(TextBox tb)
    {
        SendMessage(tb.Handle, WM_VSCROLL, (IntPtr)SB_BOTTOM, IntPtr.Zero);
    }

其他回答

In my case need to get the last line of a text file that is inserted by other application, as soon as insertion is done. Here is my solution. When the first event is raised, i disable the watcher from raising others, then i call the timer TimeElapsedEvent because when my handle function OnChanged is called i need the size of the text file, but the size at that time is not the actual size, it is the size of the file imediatelly before the insertion. So i wait for a while to proceed with the right file size.

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;
}

试试下面的代码:

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;
    }
}

如果你注册了OnChanged事件,那么在修改之前删除被监视的文件可能会起作用,只要你只需要监视OnChange事件。

试试这个!

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.
}

}

解决方案实际上取决于用例。您是否在注意不更改的新文件,或每隔一段时间更改一次的文件或经常更改的文件?在我的情况下,它的变化不太频繁,我不想错过任何这些变化。

但是我也不想在写入过程尚未完成写入的地方发生更改事件。

在我的情况下,我注意到6 (6 !!)onchange事件写一个125字符的txt文件。

我的解决方案是民意调查和改变事件的混合,民意调查经常被消极地看待。正常轮询比较慢,比如每10秒一次,以防FileSystemWatcher (FSW)“错过”一个事件。轮询立即响应FSW更改事件。

关键是在FSW。更改事件时,轮询速度加快,例如每100毫秒,并等待直到文件稳定。因此我们有了“两阶段轮询”:阶段1比较慢,但在FSW文件更改事件时立即响应。第二阶段是快速的,等待一个稳定的文件。

如果FSW检测到多个文件更改,每个事件都会加速轮询循环,并有效地启动一个新的短等待周期。只有在轮询循环检测到上次写入时文件没有进一步的变化之后,它才假定文件是稳定的,并且您的代码可以处理更改后的文件。

我选择了10秒和100毫秒的超时,但是您的用例可能需要不同的超时值。

这里是轮询,其中AppConfig。fiIO是要注意的FileInfo:

private readonly EventWaitHandle ewhTimeout = new AutoResetEvent(false);

private void TwoPhasedPolling()
{
    bool WaitForChange = true; //false: wait until stable
    DateTime LastWriteTime = DateTime.MinValue;
    while (true)
    {
        // wait for next poll (timeout), or FSW event
        bool GotOne = ewhTimeout.WaitOne(WaitForChange ? 10 * 1000 : 100);
        if (GotOne)
        {
            // WaitOne interrupted: end of Phase1: FSW detected file change
            WaitForChange = false;
        }
        else
        {
            // WaitOne timed out: Phase2: check file write time for change
            if (AppConfig.fiIO.LastWriteTime > LastWriteTime)
            {
                LastWriteTime = AppConfig.fiIO.LastWriteTime;
            }
            else
            {
                // End of Phase2: file has changed and is stable
                WaitForChange = true;
                // action on changed file
                ... your code here ...
            }}}}

private void fileSystemWatcher1_Changed(object sender, FileSystemEventArgs e)
{
    ewhTimeout.Set();
}

NB:是的,我也不喜欢}}}},但它使列表更短,这样你就不必滚动了:-)