[ IO.File ] FileSystemWatcher

本文介绍 FileSystemWatcher 的使用方法,包括如何避免缓冲区溢出、监控特定文件类型的变化、处理文件重命名及删除事件等。此外,还提供了一个利用计时器来避免因单一文件操作产生多个事件通知而导致频繁处理的例子。
    示例:

private FileSystemWatcher watcher = null;
private int _countFileChangeEvent = 0;
private static log4net.ILog log = log4net.LogManager.GetLogger(typeof(Form1));

private void Form1_Load(object sender, EventArgs e)
{
    watcher 
= new FileSystemWatcher();
    watcher.Path 
= "c:\\t"// System.AppDomain.CurrentDomain.BaseDirectory;
    watcher.IncludeSubdirectories = false;
    watcher.Filter 
= "*.xml"//"*.*"; "log4net.config";
    watcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.LastWrite | NotifyFilters.FileName;

    watcher.Changed 
+= new FileSystemEventHandler(FileChanged_Handler);
    watcher.Created 
+= new FileSystemEventHandler(FileChanged_Handler);
    watcher.Deleted 
+= new FileSystemEventHandler(FileChanged_Handler);
    watcher.Renamed 
+= new RenamedEventHandler(FileRenamed_Handler);

    watcher.EnableRaisingEvents 
= true;
}


private void FileChanged_Handler(object source, FileSystemEventArgs e)
{
    _countFileChangeEvent
++;
    log.InfoFormat(
"FileEvent {0} : {1} - {2}"
        _countFileChangeEvent.ToString(
"#00"), 
        e.ChangeType, 
        e.FullPath);
}


private void FileRenamed_Handler(object source, RenamedEventArgs e)
{
    _countFileChangeEvent
++;
    log.InfoFormat(
"FileEvent {0} : {1} - Old Path : {2} New Path : {3}"
        _countFileChangeEvent.ToString(
"#00"), 
        e.ChangeType, 
        e.OldFullPath, 
        e.FullPath);
}
    1. BufferSize
    Windows操作系统使用FileSystemWatcher创建的一个内存缓冲区通知程序文件的修改信息,如果在很短的时间内有非常多的文件修改,这个缓冲区会溢出, 造成部分追踪丢失,并且FileSystemWatcher不会产生异常。加大InternalBufferSize属性值可以避免这种情况。
    InternalBufferSize默认值是8K,可以设置的最小值是4K,增加或减小InternalBufferSize最好用4K的整数倍。每一个事件通知需要使用16字节,并不包含文件名。InternalBufferSize的内存来自non-paged内存,注意这部分内存资源比较宝贵。
    使用NotifyFilter、IncludeSubdirectories属性减小trace范围,设置filter属性并不会影响进入缓冲区的事件通知,另外尽快的完成事件处理,也是避免缓冲区溢出造成事件丢失的一个措施。
    2. 隐藏文件也会监控
    3. 有些系统中,FileSystemWatcher的事件里对长文件名使用8.3短文件名方式表示。
    4. 如果多个FileSystemWatcher在监控同一个对象,在Windows XP在没有打SP1之前,Windows 2000 SP2或之前的操作系统中,只会有一个FileSystemWatcher接收到通知;更新版本的操作系统中所有FileSystemWatcher都会收到通知。
    5. 一次文件操作产生多个事件通知
    某些文件操作可能会引发多个文件更改事件,例如新增文件、拷贝粘贴一个新的文件等。上面的示例代码使用log4net记录日志,用一个计数器记录事件编号,当Copy一个xml文件并粘贴到c:\t目录下时,从日志文件中可以看到会产生多个事件:一个Created和多个Changed。微软的解释是文件系统的操作比较复杂,另外还有其它程序的影响(例如杀毒软件等)。
    初步测试,Rename、Delete、New只会触发一个事件,Save、Paste时会有多个事件。
    在某些项目中经常需要监控某个配置文件的修改,实时加载配置信息到程序中,这种情况下可以参考log4net的做法。通过一个计时器,在文件事件处理中让计时器延迟一段时间之后,再执行加载新的配置文件操作。这样可以避免对文件做一次操作触发了多个更改事件,而多次加载配置文件。示例如下:

private FileSystemWatcher watcher = null;
private int _countFileChangeEvent = 0, _countTimerEvent = 0;
private System.Threading.Timer m_timer;
private const int TimeoutMillis = 500;
private static log4net.ILog log = log4net.LogManager.GetLogger(typeof(Form1));

private void Form1_Load(object sender, EventArgs e)
{
    watcher 
= new FileSystemWatcher();
    watcher.Path 
= "c:\\t"// System.AppDomain.CurrentDomain.BaseDirectory;
    watcher.IncludeSubdirectories = false;
    watcher.Filter 
= "*.xml"//"*.*"; "log4net.config";
    watcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.LastWrite | NotifyFilters.FileName;

    watcher.Changed 
+= new FileSystemEventHandler(FileChanged_Handler);
    watcher.Created 
+= new FileSystemEventHandler(FileChanged_Handler);
    watcher.Deleted 
+= new FileSystemEventHandler(FileChanged_Handler);
    watcher.Renamed 
+= new RenamedEventHandler(FileRenamed_Handler);

    watcher.EnableRaisingEvents 
= true;

    m_timer 
= new System.Threading.Timer(
        
new System.Threading.TimerCallback(FileChanged_TimerChanged), 
        
null
        System.Threading.Timeout.Infinite, 
        System.Threading.Timeout.Infinite);
}


private void FileChanged_Handler(object source, FileSystemEventArgs e)
{
    _countFileChangeEvent
++;
    log.InfoFormat(
"FileEvent {0} : {1} - {2}"
        _countFileChangeEvent.ToString(
"#00"), e.ChangeType, e.FullPath);
    m_timer.Change(TimeoutMillis, System.Threading.Timeout.Infinite);
}


private void FileRenamed_Handler(object source, RenamedEventArgs e)
{
    _countFileChangeEvent
++;
    log.InfoFormat(
"FileEvent {0} : {1} - Old Path : {2} New Path : {3}"
        _countFileChangeEvent.ToString(
"#00"), e.ChangeType, e.OldFullPath, e.FullPath);
    m_timer.Change(TimeoutMillis, System.Threading.Timeout.Infinite);
}


private void FileChanged_TimerChanged(object state)
{
    
this.FileChanged();
}


private void FileChanged()
{
    _countTimerEvent
++;
    log.InfoFormat(
"TimerEvent {0}",_countTimerEvent.ToString("#00"));
}
    上面例子用log4net分别记录了FileSystemWatcher事件和Timer的Change事件,这样可以做一个简单的分析。
    操作如下:对一个文件重命名,拷贝粘贴一个文件,打开一个文件进行修改后保存,选择2个文件删除,记录的log如下:
2006-12-16 22:50:07,828 [2904] - FileEvent 01 : Renamed - Old Path : c:\t\a.xml New Path : c:\t\b.xml
2006-12-16 22:50:08,359 [3828] - TimerEvent 01
2006-12-16 22:50:49,171 [2904] - FileEvent 02 : Created - c:\t\Copy of b.xml
2006-12-16 22:50:49,171 [2904] - FileEvent 03 : Changed - c:\t\Copy of b.xml
2006-12-16 22:50:49,171 [2904] - FileEvent 04 : Changed - c:\t\Copy of b.xml
2006-12-16 22:50:49,671 [3828] - TimerEvent 02
2006-12-16 22:51:02,312 [2904] - FileEvent 05 : Changed - c:\t\1.xml
2006-12-16 22:51:02,312 [2904] - FileEvent 06 : Changed - c:\t\1.xml
2006-12-16 22:51:02,812 [3828] - TimerEvent 03
2006-12-16 22:51:15,250 [2904] - FileEvent 07 : Deleted - c:\t\1.xml
2006-12-16 22:51:15,250 [2904] - FileEvent 08 : Deleted - c:\t\Copy of b.xml
2006-12-16 22:51:15,750 [3828] - TimerEvent 04
    可以看到,一个文件操作引起多个文件更改事件时,Timer的Change事件都只执行了一次。
    6. 有人想同时监控多种类型的文件,例如*.xml + *.config,发现Filter属性不支持这种设置(只能够设置一种)。
    这种情况下可以将Filter属性设成*.*,在事件里用if (e.FullPath.EndsWith(".xml") || e.FullPath.EndsWith(".config"))自己判断过滤一下。记得Filter属性的设置并不会减少进入缓冲区的事件通知,因此上面的方法并不会带来多少性能损失。
Framework 版本: v4.0.30319 说明: 由于未经处理的异常,进程终止。 异常信息: System.IO.DirectoryNotFoundException 在 System.IO.__Error.WinIOError(Int32, System.String) 在 System.IO.FileStream.Init(System.String, System.IO.FileMode, System.IO.FileAccess, Int32, Boolean, System.IO.FileShare, Int32, System.IO.FileOptions, SECURITY_ATTRIBUTES, System.String, Boolean, Boolean, Boolean) 在 System.IO.FileStream..ctor(System.String, System.IO.FileMode, System.IO.FileAccess, System.IO.FileShare, Int32, System.IO.FileOptions, System.String, Boolean, Boolean, Boolean) 在 System.IO.StreamWriter.CreateFile(System.String, Boolean, Boolean) 在 System.IO.StreamWriter..ctor(System.String, Boolean, System.Text.Encoding, Int32, Boolean) 在 System.IO.StreamWriter..ctor(System.String, Boolean) 在 Mabinogi.Network.ReadPacket.WriteFileLog(System.String, System.String) 在 Mabinogi.Network.ServerCommInstance+AsynchReceiveObject.Receive(System.IAsyncResult) 在 System.Net.LazyAsyncResult.Complete(IntPtr) 在 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) 在 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) 在 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) 在 System.Net.ContextAwareResult.Complete(IntPtr) 在 System.Net.LazyAsyncResult.ProtectedInvokeCallback(System.Object, IntPtr) 在 System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(UInt32, UInt32, System.Threading.NativeOverlapped*) 在 System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)
最新发布
03-21
public class CustomerWatcher { System.IO.FileSystemWatcher watcher = new System.IO.FileSystemWatcher(); List<string> currentpathList= new List<string>(); public void Watch(string Path) { watcher.Path = Path; watcher.NotifyFilter = System.IO.NotifyFilters.LastAccess | System.IO.NotifyFilters.LastWrite | System.IO.NotifyFilters.FileName | System.IO.NotifyFilters.DirectoryName; watcher.Changed += new System.IO.FileSystemEventHandler(OnChanged); watcher.Created += new System.IO.FileSystemEventHandler(OnCreated); watcher.Deleted += new System.IO.FileSystemEventHandler(OnChanged); watcher.Renamed += new System.IO.RenamedEventHandler(OnRenamed); watcher.EnableRaisingEvents = true; } private void OnChanged(object source, System.IO.FileSystemEventArgs e) { Console.WriteLine("File: " + e.FullPath + " " + e.ChangeType); string filePath = e.FullPath; List<string> filePathList = filePath.Split("\\").ToList(); int filePathListCount = filePathList.Count(); if (filePathListCount < 6) { return; } else if (filePathListCount > 6) { for(int i= filePathListCount - 1;i>=6;i--) { filePathList.RemoveAt(i); } } string productRootPath = string.Join("\\", filePathList); if(currentpathList.Contains(productRootPath)) { return; } int delayTime = 300; Thread.Sleep(delayTime); string cust_code=filePathList[2]; string cusr_code_short = cust_code.Split('(')[0]; if(cusr_code_short=="AZ") { } currentpathList.Add(productRootPath); }修改代码,中间这部分代码放到新的线程里面运行,不要阻塞主线程
03-20
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值