【原创】C#文件监视--采用filesystemwatcher 和 system.Timer.Timer
我的一个windows服务要用于和其他软件通信,采用的方法是比较简单的文件通信方式,就是大家都访问一个文件夹,对方将要处理的文件放到该文件夹里,然后我来监控这个文件夹,发现有我要的文件我就取走处理。
通过万能的baidu和Google,我找到了FileSystemWatcher这么一个类可以来处理文件监视,用法也很简单,主要代码如下:
//Create a new FileSystemWatcher and set its properties.
watcher = new FileSystemWatcher();
watcher.Path = pathName;
/* Watch for changes in LastAccess and LastWrite times, and
the renaming of files or directories. */
watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName;
// Only watch text files.
watcher.Filter = "*.txt";
// Add event handlers.
watcher.Created += new FileSystemEventHandler(OnChanged);
watcher.Error += new ErrorEventHandler(OnError);
// Begin watching.
watcher.EnableRaisingEvents = true;
只要给这个类的对象设置好监视的路径,监视文件有什么属性改变,还有监视文件的类型,最后添加事件触发的处理事件就可以了,当然记得一定要激活该对象(就是watcher.EnableRaisingEvents = true;这句话)。这个类用于监视本地文件夹的时候,工作的很好,但是我的程序需求是还要能监视网络文件夹,也就是网络上有个共享文件夹,当我用这个类来监视网络文件夹的时候就出现了一些不能解决的问题,
主要的问题就是当我网络出现一些问题,比如我拔掉网线,然后再过一会(大概过个几分钟吧,太短是不会出问题)插回去,就发现我的watcher失效了,按照MSDN上面的说法是,可以通过添加ErrorEventhandler来捕获watcher出现的异常事件,在Google上面也搜到有些人的帖子说确实可以捕获到错误,然后再错误处理里面来重新创建watcher,但是很遗憾的是我的拔网线测试始终通不过,但是很有意思的是通过禁用本机的网卡,可以捕获到异常,但是禁用监视文件夹所在机器的网卡还是捕获不到异常,所以我得出的结论是---建议在使用本地文件夹监视的时候可以采用FileSystemWatcher,网络文件夹监视最好不要使用。
既然FileSystemWatcher不能很好的监控网络文件夹,那么应该如何监控网络的文件夹呢,因为当前的服务程序里面有用到定时器,就很自然的想到用定时扫描文件来监视文件夹,虽然方法看上去很笨,但是效果却很不错,以下是代码:
public class FolderWatchHelper { public string m_pathName { set; get; } public string m_fileTypeFilter { set; get; } public string m_logName { set; get; } public string m_logPassword { set; get; } public List<string> m_fileList { set; get; } private System.Timers.Timer m_timer; public FolderWatchHelper(float interval) { m_fileList = new List<string>(); m_timer = new System.Timers.Timer(); m_timer.Interval = interval; m_timer.Elapsed += new System.Timers.ElapsedEventHandler(WatchFolder);//到达时间的时候执行事件; m_timer.AutoReset = true;//设置是执行一次(false)还是一直执行(true); m_timer.Enabled = false;//是否执行System.Timers.Timer.Elapsed事件; } public bool IsRun() { return m_timer.Enabled; } public bool Run() { bool ret = true; if (m_timer == null) { ret = false; } else if (m_timer.Enabled == false) { m_timer.Enabled = true;//是否执行System.Timers.Timer.Elapsed事件; } return ret; } public bool Stop() { bool ret = true; if (m_timer != null && m_timer.Enabled == true) { m_timer.Enabled = false; } return ret; } public void WatchFolder(object source, System.Timers.ElapsedEventArgs e) { m_timer.Enabled = false; bool ret = true; if (m_pathName != "") { if (!Directory.Exists(m_pathName)) { if (m_logName != "" || m_logPassword != "") { ConnAuthorize conn = new ConnAuthorize(m_logName, m_logPassword, m_pathName); ret = conn.RemoteConnect(true); } } if (Directory.Exists(m_pathName)) { lock (m_fileList) { DirectoryInfo folder = new DirectoryInfo(m_pathName); foreach (FileInfo file in folder.GetFiles(m_fileTypeFilter)) { string fileName = file.FullName; if (!m_fileList.Contains(fileName)) { m_fileList.Add(file.FullName); } } } } else { LogHelper.WriteLogFile("the watchfolder is not exist."); } } m_timer.Enabled = true; } }
最主要的就是定时处理函数了public void WatchFolder(object source, System.Timers.ElapsedEventArgs e),这里面就是去读取路径下过滤类型的文件夹,然后放入链表,之所以有用户民和密码,那是因为有些远程共享文件即使设置everyone共享但是第一次访问还是要用用户名和密码来创立连接,该功能代码也贴出来吧:
class ConnAuthorize { private string userName; private string userPwd; private string shareResDictionary; // constructor public ConnAuthorize(string myUserName, string myUserPwd, string myShareResDictionary) { this.userName = myUserName; this.userPwd = myUserPwd; this.shareResDictionary = myShareResDictionary; } [StructLayout(LayoutKind.Sequential)] public struct NETRESOURCEA { public int dwScope; public int dwType; public int dwDisplayType; public int dwUsage; [MarshalAs(UnmanagedType.LPStr)] public string lpLocalName; [MarshalAs(UnmanagedType.LPStr)] public string lpRemoteName; [MarshalAs(UnmanagedType.LPStr)] public string lpComment; [MarshalAs(UnmanagedType.LPStr)] public string lpProvider; public override String ToString() { String str = "LocalName: " + lpLocalName + " RemoteName: " + lpRemoteName + " Comment: " + lpComment + " lpProvider: " + lpProvider; return (str); } } [DllImport("mpr.dll")] public static extern int WNetAddConnection2([MarshalAs(UnmanagedType.LPArray)] NETRESOURCEA[] lpNetResource, [MarshalAs(UnmanagedType.LPStr)] string lpPassword, [MarshalAs(UnmanagedType.LPStr)] string UserName, int dwFlags); [DllImport("mpr.dll")] public static extern int WNetCancelConnection2(string lpName, int dwFlags, bool fForce); [DllImport("mpr.dll")] public static extern int WNetOpenEnum(int dwScope, int dwType, int dwUsage, [MarshalAs(UnmanagedType.LPArray)] NETRESOURCEA[] lpNetResource, ref IntPtr lphEnum); public bool RemoteConnect(bool bConnected) { int res; try { NETRESOURCEA[] n = new NETRESOURCEA[1]; n[0] = new NETRESOURCEA(); n[0].dwType = 1; int dwFlags = 1; // CONNECT_INTERACTIVE; n[0].lpLocalName = @""; n[0].lpRemoteName = shareResDictionary; n[0].lpProvider = null; if (bConnected) { res = WNetAddConnection2(n, userPwd, userName, dwFlags); } else { res = WNetCancelConnection2(shareResDictionary, 1, true); } } catch (Exception e) { res = 1; LogHelper.WriteLogFile("SQLiteBackUpService ConnAuthorize:RemoteConnect exception,e info:" + e.ToString()); } if (res == 1219) { res = 0; } return (res == 0) ? true : false; } }
通过定时器监视共享文件可以很好的监视本地和网络文件夹,即使断网只要网络恢复程序任然能够正常监视。