我有一个应用程序,我正在寻找一个文本文件,如果对文件做了任何更改,我使用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并保存它。
很抱歉挖了坟墓,但我已经与这个问题斗争了一段时间,终于想出了一种方法来处理这些多重发射事件。我想要感谢这篇文章中的每一个人,因为我在与这个问题作斗争时已经在许多参考文献中使用了它。
这是我的完整代码。它使用字典来跟踪文件最后一次写入的日期和时间。它比较该值,如果值相同,则抑制事件。然后在启动新线程后设置该值。
using System.Threading; // used for backgroundworker
using System.Diagnostics; // used for file information
private static IDictionary<string, string> fileModifiedTable = new Dictionary<string, string>(); // used to keep track of our changed events
private void fswFileWatch_Changed( object sender, FileSystemEventArgs e )
{
try
{
//check if we already have this value in our dictionary.
if ( fileModifiedTable.TryGetValue( e.FullPath, out sEmpty ) )
{
//compare timestamps
if ( fileModifiedTable[ e.FullPath ] != File.GetLastWriteTime( e.FullPath ).ToString() )
{
//lock the table
lock ( fileModifiedTable )
{
//make sure our file is still valid
if ( File.Exists( e.FullPath ) )
{
// create a new background worker to do our task while the main thread stays awake. Also give it do work and work completed handlers
BackgroundWorker newThreadWork = new BackgroundWorker();
newThreadWork.DoWork += new DoWorkEventHandler( bgwNewThread_DoWork );
newThreadWork.RunWorkerCompleted += new RunWorkerCompletedEventHandler( bgwNewThread_RunWorkerCompleted );
// capture the path
string eventFilePath = e.FullPath;
List<object> arguments = new List<object>();
// add arguments to pass to the background worker
arguments.Add( eventFilePath );
arguments.Add( newEvent.File_Modified );
// start the new thread with the arguments
newThreadWork.RunWorkerAsync( arguments );
fileModifiedTable[ e.FullPath ] = File.GetLastWriteTime( e.FullPath ).ToString(); //update the modified table with the new timestamp of the file.
FILE_MODIFIED_FLAG.WaitOne(); // wait for the modified thread to complete before firing the next thread in the event multiple threads are being worked on.
}
}
}
}
}
catch ( IOException IOExcept )
{
//catch any errors
postError( IOExcept, "fswFileWatch_Changed" );
}
}
试试下面的代码:
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;
}
}
我简单地添加了一个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);
}
事件如果没有问,这是一个遗憾,没有现成的解决方案样本f#。
要解决这个问题,这里有我的方法,因为我可以,而且f#是一种很棒的。net语言。
使用FSharp.Control.Reactive包过滤掉重复的事件,它只是响应式扩展的f#包装器。所有这些都可以针对全框架或netstandard2.0:
let createWatcher path filter () =
new FileSystemWatcher(
Path = path,
Filter = filter,
EnableRaisingEvents = true,
SynchronizingObject = null // not needed for console applications
)
let createSources (fsWatcher: FileSystemWatcher) =
// use here needed events only.
// convert `Error` and `Renamed` events to be merded
[| fsWatcher.Changed :> IObservable<_>
fsWatcher.Deleted :> IObservable<_>
fsWatcher.Created :> IObservable<_>
//fsWatcher.Renamed |> Observable.map renamedToNeeded
//fsWatcher.Error |> Observable.map errorToNeeded
|] |> Observable.mergeArray
let handle (e: FileSystemEventArgs) =
printfn "handle %A event '%s' '%s' " e.ChangeType e.Name e.FullPath
let watch path filter throttleTime =
// disposes watcher if observer subscription is disposed
Observable.using (createWatcher path filter) createSources
// filter out multiple equal events
|> Observable.distinctUntilChanged
// filter out multiple Changed
|> Observable.throttle throttleTime
|> Observable.subscribe handle
[<EntryPoint>]
let main _args =
let path = @"C:\Temp\WatchDir"
let filter = "*.zip"
let throttleTime = TimeSpan.FromSeconds 10.
use _subscription = watch path filter throttleTime
System.Console.ReadKey() |> ignore
0 // return an integer exit code