前言:FileSystemWatcher本身是一个多线程的组件,通过TreadPool实现监控多个文件,异步触发我们关注的事件。这里我们通过反编译 FileSystemWatcher来看看它的实现机制,能否监视伪造文件,I/O操作怎样等。这里采用的是.Net Framework 4.0的版本。注意:我在msdn上查到一个修复:
当在 Microsoft.NET Framework 4 基于应用程序使用FileSystemWatcher类,以监视指定的目录中的更改时,则会发生内存泄漏。 FileSystemWatcher类中存在错误,会发生此问题。当创建一个FileSystemWatcher对象时, FileSystemWatcher类使用GCHandle类创建固定的垃圾回收器 (GC) 句柄。句柄是 8k 字节为单位),并使用字节 [] 数据格式。但是, FileSystemWatcher对象永远不会释放垃圾回收器句柄。
这里需要安装 4.0.30319.513 以后的版本进行修复。查看这里
[PermissionSet(SecurityAction.LinkDemand, Name="FullTrust")] static void Main(string[] args) { FileSystemWatcher watcher = new FileSystemWatcher(); watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; watcher.Filter = "*.*"; watcher.Path = "D:\\test"; watcher.Changed += new FileSystemEventHandler(OnChanged); watcher.Created += new FileSystemEventHandler(OnChanged); watcher.Deleted += new FileSystemEventHandler(OnChanged); watcher.Renamed += new RenamedEventHandler(OnRenamed); watcher.EnableRaisingEvents = true; Console.WriteLine("Press \'q\' to quit the sample."); while (Console.Read() != 'q') ; } private static void OnChanged(object source, FileSystemEventArgs e) { Console.WriteLine("File: " + e.FullPath + " " + e.ChangeType); } private static void OnRenamed(object source, RenamedEventArgs e) { Console.WriteLine("File: {0} renamed to {1}", e.OldFullPath, e.FullPath); }比较重要的就是Changed事件,NotifyFilter属性,由于引发Changed事件是非常复杂,我们需要使用NotifyFilter过滤我们关注的更改事件,否则会发生内存溢出的可能。
Windows 操作系统在 FileSystemWatcher 创建的缓冲区中通知组件文件发生更改。 如果短时间内有很多更改,则缓冲区可能会溢出。 这将导致组件失去对目录更改的跟踪,并且它将只提供一般性通知。 增加缓冲区的大小与 InternalBufferSize 属性的是成本高昂的,那么,当有来自不能交换到磁盘的未调用的内存,因此,保留缓冲区如小,并且足以不缺少任何文件更改事件。 若要避免缓冲区溢出,请使用 NotifyFilter 和 IncludeSubdirectories 属性,以便可以筛选掉不想要的更改通知。InternalBufferSize 最小是 4K,如果设置小于4k会修改为4k;如果不设置,默认是8k;最大可设置为64k。设置越大,系统花费的代价就会高。
反编译 FileSystemWatcher后,主要关注到这两个私有的方法:
private unsafe void CompletionStatusChanged(uint errorCode, uint numBytes, NativeOverlapped* overlappedPointer); private unsafe void Monitor(byte[] buffer);
可以看到,这两个方法都是调用非托管的系统方法,来实现监视的功能。CompletionStatusChanged 主要是异步通知事件的实现,Monitor才是真正的监视。
Monitor方法中调用了这个本地方法::
flag = UnsafeNativeMethods.ReadDirectoryChangesW(this.directoryHandle, new HandleRef(this, (IntPtr) numRef), this.internalBufferSize, this.includeSubdirectories ? 1 : 0, (int) this.notifyFilters, out num, overlappedPointer, NativeMethods.NullHandleRef);
看到这个非托管方法没有:ReadDirectoryChangesW,我们在上一篇文章提到的,操作系统级别的监视能力。
那么这个方法是什么意思呢?详细查看这里。简单来说,这是一种依托于windows 的消息机制来实现监视反馈的,通过查略的资料,这种方式应当是windows下最优的方案了,当然只是最优是基于性能和实现上的平衡。
这里我查略的资料都是通过搜索引擎来的,如果需要实际应用还是查略MSDN更好,由于我对Win32 的API不太熟悉,所以无法深入讨论。大家可以给我些这样的资料学习下,指教下。
ReadDirectoryChangesW,对于伪造文件,同样大小,同样的修改时间和访问时间,是否可以通知?这里我还存有疑问。唯一的办法时候只有识别内容,计算md5 码类似方式,但似乎又不太现实。不知道大家更否告诉一下呢?