Windows服务FileSystemWatcher触发InputOnChanged时崩溃求助
问题排查与修复方案
核心问题分析
你的Windows服务在监控目录添加文件时崩溃,且InputOnChanged中的日志无法写入,主要源于以下几点:
- FileSystemWatcher事件特性:
Changed事件会因文件写入的中间操作(如临时缓存、多次保存)触发多次,且首次触发时文件可能仍被写入进程锁定,甚至已被意外删除,导致后续文件操作报错。 - 异常捕获不完整:当前代码仅捕获了文件处理逻辑内的异常,方法开头的日志写入等操作若抛出异常,会直接导致服务崩溃且无日志记录。
- 文件操作时机问题:在未确保文件完全写入、释放锁定的情况下就执行复制、读取操作,极易引发「文件不存在」或「文件被占用」异常。
修复步骤与代码优化
1. 完整捕获所有异常
将InputOnChanged的全部逻辑包裹在顶层try-catch中,确保任何环节的异常都能被捕获并记录,避免服务崩溃。同时在捕获时记录完整堆栈信息,便于定位问题。
2. 处理文件锁定与延迟触发
添加短暂延迟等待文件完全写入,且在文件操作前再次检查文件存在性;优先使用Created事件替代Changed事件(更适合监控新添加的文件)。
3. 确保目录存在
在执行文件复制、写入前,先检查并创建目标目录,避免因目录不存在引发异常。
修改后的代码示例
protected void InputOnChanged(object source, FileSystemEventArgs e) { // 顶层捕获所有异常,防止服务崩溃 try { if (e.ChangeType != WatcherChangeTypes.Changed) return; eventLog.WriteEntry($"Change Detected - File {e.Name}", EventLogEntryType.Information); // 延迟1秒,等待文件完全写入并释放锁定 System.Threading.Thread.Sleep(1000); // 再次校验文件存在性 if (!File.Exists(e.FullPath)) { eventLog.WriteEntry($"{e.Name} was not accessible at processing time", EventLogEntryType.Error); return; } try { fileWatcherService.EnableRaisingEvents = false; eventLog.WriteEntry("Starting process for file: " + e.Name); // 备份文件处理:确保目标目录存在 string backupDir = Path.Combine(@"\\XXXXXX\XXXXXX\XXXXXXXX\XXXXXXXXXX XXX XXXXXXXXX\XXX\XXXX\XXXX\BackupFiles"); Directory.CreateDirectory(backupDir); string backupFilePath = Path.Combine(backupDir, GetBackupFileName(e.Name)); File.Copy(e.FullPath, backupFilePath, false); // 格式化文件处理:确保目标目录存在 string formattedDir = Path.Combine(@"\\XXXXXX\XXXXXX\XXXXXXXX\XXXXXXXXXX XXX XXXXXXXXX\XXX\XXXX\XXXX\FormattedFiles"); Directory.CreateDirectory(formattedDir); string formattedFilePath = Path.Combine(formattedDir, GetFormattedFileName(e.Name)); List<string> lines = System.IO.File.ReadAllLines(e.FullPath).ToList(); File.WriteAllText(formattedFilePath, CSVFormatService.FormatLines(lines)); // 删除原文件 File.Delete(e.FullPath); eventLog.WriteEntry($"Successfully processed and deleted {e.FullPath}", EventLogEntryType.Information); } catch (IOException ioEx) { // 单独捕获IO异常,更精准定位文件操作问题 eventLog.WriteEntry($"IO error processing {e.Name}: {ioEx.Message}\nStack Trace: {ioEx.StackTrace}", EventLogEntryType.Error); } catch (Exception ex) { eventLog.WriteEntry($"Service exception processing {e.Name}: {ex.Message}\nStack Trace: {ex.StackTrace}", EventLogEntryType.Error); } finally { fileWatcherService.EnableRaisingEvents = true; } } catch (Exception ex) { // 极端情况:若eventLog实例不可用,使用应急日志写入 using (var emergencyLog = new EventLog("Application")) { emergencyLog.Source = "YourCustomSourceName"; // 替换为你的自定义事件源名称 emergencyLog.WriteEntry($"Critical error in InputOnChanged: {ex.Message}\nStack Trace: {ex.StackTrace}", EventLogEntryType.Error); } } }
额外排查建议
- 权限校验:确认服务运行的域用户对监控目录、备份目录、格式化目录拥有读、写、删除权限,包括共享目录的NTFS权限和共享权限。
- 事件日志排查:在事件查看器的「系统」日志中查找服务崩溃的事件(事件ID通常为7034),获取崩溃的详细调用栈。
- 替换事件类型:将
FileSystemWatcher的监听事件从Changed改为Created,该事件在文件完全创建完成后触发,更适合处理新添加的文件,减少异常概率。 - 队列化处理:将待处理的文件路径加入线程安全队列,由单独的后台线程处理文件操作,避免EventLog的线程上下文问题,同时防止事件重复触发导致的重复处理。
内容的提问来源于stack exchange,提问作者Jacob Boyd
相关产品推荐
相关产品推荐

