PowerShell中FileSystemWatcher动作期间暂停与重启方案问询
解决FileSystemWatcher事件排队与重启问题
我明白你的痛点——在处理信号灯的60秒等待期内,新的文件创建事件会排队,导致信号灯刚恢复就又变红,而且你尝试停止后重启Watcher失败。核心问题在于你之前错误地Dispose了Watcher对象(一旦Dispose就无法复用),同时缺少复用Watcher配置的逻辑导致代码冗余。
解决方案思路
- 封装Watcher配置为函数:把创建和配置FileSystemWatcher的逻辑抽成函数,这样重启时直接调用即可,避免重复代码。
- 暂停而非销毁Watcher:处理事件时,只需要将
EnableRaisingEvents设为$false暂停监听,处理完逻辑后再设为$true恢复,不需要Dispose对象(Dispose会彻底释放,无法再次使用)。 - 避免事件队列堆积:暂停监听后,新的文件创建事件不会被加入队列,直到恢复监听才会重新检测。
修改后的完整代码
Write-Host "Auto Relay Change On Detection" Add-Type -AssemblyName PresentationCore,PresentationFramework $settingsFile = "$PSScriptRoot\settings.json" $j = Get-Content -Raw -Path $settingsFile | ConvertFrom-Json $PathToMonitor = $j.imageFolder If(!(test-path $PathToMonitor)) { New-Item -ItemType Directory -Force -Path $PathToMonitor } # 封装FileSystemWatcher配置逻辑,方便复用 function Initialize-FileSystemWatcher { param( [string]$Path, [bool]$IncludeSubdirectories = $true ) $watcher = New-Object System.IO.FileSystemWatcher $watcher.Path = $Path $watcher.IncludeSubdirectories = $IncludeSubdirectories $watcher.EnableRaisingEvents = $true return $watcher } # 初始化Watcher $FileSystemWatcher = Initialize-FileSystemWatcher -Path $PathToMonitor # 定义文件变更检测时执行的代码 $Action = { $details = $event.SourceEventArgs $Name = $details.Name $FullPath = $details.FullPath $ChangeType = $details.ChangeType $Timestamp = $event.TimeGenerated switch ($ChangeType) { 'Created' { $text = "File {0} was created." -f $Name Write-Host $text -ForegroundColor Yellow Write-Host "Relay = detected at $(Get-Date)" -ForegroundColor Yellow # 暂停Watcher事件监听,避免新事件排队 $event.MessageData.Watcher.EnableRaisingEvents = $false try { # 执行API调用(替换成你的继电器控制代码) $timeout = $event.MessageData.Timer Start-Sleep -s $timeout # 执行恢复绿色的API调用 Write-Host "Relay = default at $(Get-Date)" -ForegroundColor Yellow } finally { # 无论是否出错,都恢复Watcher监听 $event.MessageData.Watcher.EnableRaisingEvents = $true } } 'Changed' { Write-Host "CHANGE: $FullPath" } 'Deleted' { Write-Host "DELETED: $FullPath" } 'Renamed' { Write-Host "RENAMED: $FullPath" } default { Write-Host $_ -ForegroundColor Red -BackgroundColor White } } } # 添加事件处理器时,传递Watcher和定时器参数给Action $handlers = Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Created -Action $Action -SourceIdentifier FSCreate -MessageData @{ Watcher = $FileSystemWatcher Timer = $j.timer } try { do { Wait-Event -Timeout 1 } while ($true) } finally { Unregister-Event -SourceIdentifier FSCreate $handlers | Remove-Job $FileSystemWatcher.EnableRaisingEvents = $false $FileSystemWatcher.Dispose() Write-Host "Event Handler disabled." }
关键修改说明
- 封装Initialize-FileSystemWatcher函数:把Watcher的创建和配置逻辑抽离,后续如果需要重启(比如异常后),直接调用这个函数即可,避免代码重复。
- Action中操作Watcher状态:通过
-MessageData将Watcher对象和定时器参数传递给Action脚本块,这样在处理事件时可以直接修改EnableRaisingEvents来暂停/恢复监听,不需要重新创建Watcher。 - try/finally确保监听恢复:即使处理过程中出现错误,也会通过finally块恢复Watcher的监听状态,避免程序陷入无响应。
- 避免Dispose重启:不再在Action中Dispose Watcher,只是切换
EnableRaisingEvents状态,这样Watcher对象可以持续复用,解决重启失败的问题。
这样修改后,当有文件创建时,Watcher会暂停监听,直到60秒信号灯恢复后才重新开始检测新文件,彻底解决事件排队的问题,同时代码也更简洁易维护。
内容的提问来源于stack exchange,提问作者lennert_h
相关产品推荐
相关产品推荐

