Зависание Powershell из-за Filesystemwatcher - PullRequest
2 голосов
/ 05 июня 2019

У нас есть запутанное решение некоторых проблем печати (вызванных Citrix и удаленными серверами). В основном с основного сервера мы принудительно отправляем pdf-файл на удаленный компьютер, а затем на удаленном компьютере постоянно запускается скрипт powershell, который «ловит» файл и передает его на локальный принтер

Это работает "отлично"

Однако мы получаем случайные отсева. Сценарий powershell, похоже, не аварийно завершился, потому что он все еще работает в Windows, но реальное действие, похоже, не обрабатывает новые файлы

Сегодня я произвел ОЧЕНЬ МНОГО чтения, и есть упоминание о необходимости именовать и отменять регистрацию событий, когда вы закончите, иначе это может вызвать проблемы переполнения буфера и заставить PowerShell прекратить обработку действия. Но я не уверен, куда это должно идти в коде. Идея состоит в том, что этот скрипт будет работать постоянно, поэтому мы отменяем регистрацию или удаляем событие внутри самого действия или где-то еще?

У меня до этого в действии было МНОЖЕСТВО фиктивной регистрации, чтобы попытаться найти причину неудачи, но, кажется, она останавливается в разных точках без какой-либо уважительной причины (т. Е. Команда не может найти файлы, другие время на команду, чтобы двигаться и т. д.)

### SET FOLDER TO WATCH + FILES TO WATCH + SUBFOLDERS YES/NO
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "l:\files\cut"
$watcher.Filter = "*.pdf"
$watcher.IncludeSubdirectories = $false
$watcher.EnableRaisingEvents = $true  

### DEFINE ACTIONS AFTER AN EVENT IS DETECTED
$action = { $path = $Event.SourceEventArgs.FullPath
            $changeType = $Event.SourceEventArgs.ChangeType
            $scandir="l:\files\cut" 
            $scanbackdir="l:\files\cut\back"   
            $scanlogdir="l:\files\cut\log" 
            $sumatra="l:\SumatraPDF.exe"      
            $pdftoprint=""
            $printername= "MainLBL"

### Get the List of files in the Directory, print file, wait and then move file
Get-ChildItem -Path $scandir -filter "*.pdf" -Name | % { 
$pdftoprint=$_ 
& $sumatra -silent $scandir\$pdftoprint -print-to $printername

sleep 3 

Move-Item -force $scandir\$pdftoprint $scanbackdir
 }
 } 

### Define what happens when script fails
$erroraction = {echo $(get-date) the process crashed | Out-File -Append l:\files\cut\log\errorlog.txt}    

### DECIDE WHICH EVENTS SHOULD BE WATCHED 
Register-ObjectEvent $watcher "Error" -Action $erroraction
Register-ObjectEvent $watcher "Created" -Action $action
while ($true) {sleep 5}

Ответы [ 2 ]

1 голос
/ 05 июня 2019

Если вы хотите, чтобы скрипт выполнялся в фоновом режиме, посмотрите фоновые задания PowerShell.

Если вы хотите, чтобы скрипт выполнялся постоянно, то вы хотите сделать его службой ...

См. Эти:

Как создать пользовательскую службу

Как запустить скрипт PowerShell в качестве службы Windows

... или присоедините его к запланированному заданию, которое перезапустит его при перезагрузке.

Существует два способа реализации FileSystemWatcher.

  • Синхронный
  • Асинхронный

Синхронный FileSystemWatcher по своей природе, когда обнаруживается изменение, элемент управления возвращается вашему сценарию, чтобы он мог обработать изменение. Если другое изменение файла происходит, когда ваш сценарий больше не ожидает событий, он теряется. Следовательно, приводит к неожиданным результатам.

При асинхронном использовании FileSystemWatcher он продолжит регистрировать новые изменения файловой системы и обрабатывать их, когда PowerShell завершит обработку предыдущих изменений.

* Пример - пример асинхронного FileSystemWatcher *

### New-FileSystemWatcherAsynchronous

# Set the folder target
$PathToMonitor = Read-Host -Prompt 'Enter a folder path'

$FileSystemWatcher = New-Object System.IO.FileSystemWatcher
$FileSystemWatcher.Path  = $PathToMonitor
$FileSystemWatcher.IncludeSubdirectories = $true

# Set emits events
$FileSystemWatcher.EnableRaisingEvents = $true

# Define change actions
$Action = {
    $details = $event.SourceEventArgs
    $Name = $details.Name
    $FullPath = $details.FullPath
    $OldFullPath = $details.OldFullPath
    $OldName = $details.OldName
    $ChangeType = $details.ChangeType
    $Timestamp = $event.TimeGenerated
    $text = "{0} was {1} at {2}" -f $FullPath, $ChangeType, $Timestamp

    Write-Host $text -ForegroundColor Green

    # Define change types
    switch ($ChangeType)
    {
        'Changed' { "CHANGE" }
        'Created' { "CREATED"}
        'Deleted' { "DELETED"
                    # Set time intensive handler
                    Write-Host "Deletion Started" -ForegroundColor Gray
                    Start-Sleep -Seconds 3    
                    Write-Warning -Message 'Deletion complete'
                  }
        'Renamed' { 
                    $text = "File {0} was renamed to {1}" -f $OldName, $Name
                    Write-Host $text -ForegroundColor Yellow
                  }
        default { Write-Host $_ -ForegroundColor Red -BackgroundColor White }
    }
}

# Set event handlers
$handlers = . {
    Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Changed -Action $Action -SourceIdentifier FSChange
    Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Created -Action $Action -SourceIdentifier FSCreate
    Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Deleted -Action $Action -SourceIdentifier FSDelete
    Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Renamed -Action $Action -SourceIdentifier FSRename
}

Write-Host "Watching for changes to $PathToMonitor" -ForegroundColor Cyan

try
{
    do
    {
        Wait-Event -Timeout 1
        Write-Host '.' -NoNewline

    } while ($true)
}
finally
{
    # End script actions + CTRL+C executes the remove event handlers
    Unregister-Event -SourceIdentifier FSChange
    Unregister-Event -SourceIdentifier FSCreate
    Unregister-Event -SourceIdentifier FSDelete
    Unregister-Event -SourceIdentifier FSRename

    # Remaining cleanup
    $handlers | 
    Remove-Job

    $FileSystemWatcher.EnableRaisingEvents = $false
    $FileSystemWatcher.Dispose()

    Write-Warning -Message 'Event Handler completed and disabled.'
}
0 голосов
/ 05 июня 2019

Я не встречал сценарий, который будет постоянно работать в Windows. Поэтому, учитывая это, мы считаем само собой разумеющимся, что возникнут некоторые проблемы, не зависящие от вас, такие как сеть , отключение питания или отключение системы. Имея это в виду, у нас есть жизненный цикл для этого скрипта, и все должно быть должным образом очищено в конце. В этом случае у нас есть цикл while, который теоретически никогда не должен заканчиваться, однако, если выброшено исключение, оно закончится. В рамках цикла while, если какое-либо из событий было отменено, мы можем перерегистрировать их. Если наблюдатель был удален, мы можем воссоздать его и события. Если это действительно критически важный код, то я бы посмотрел на .net как альтернативу с чем-то вроде hangfire с nlog в качестве службы Windows.

### WRAP Everything in a try finally so we dispose of events 
try {
    ### SET FOLDER TO WATCH + FILES TO WATCH + SUBFOLDERS YES/NO
    $watcherArgs = @{
        Path = "l:\files\cut"
        Filter = "*.pdf"
        IncludeSubdirectories = $false
        EnableRaisingEvents = $true  
    }
    $watcher = New-Object System.IO.FileSystemWatcher
    $watcher.Path = $watcherArgs.Path
    $watcher.Filter = $watcherArgs.Filter
    $watcher.IncludeSubdirectories = $watcherArgs.IncludeSubdirectories
    $watcher.EnableRaisingEvents = $watcherArgs.EnableRaisingEvents

    ### DEFINE ACTIONS AFTER AN EVENT IS DETECTED
    $action = { $path = $Event.SourceEventArgs.FullPath
                $changeType = $Event.SourceEventArgs.ChangeType
                $scandir="l:\files\cut" 
                $scanbackdir="l:\files\cut\back"   
                $scanlogdir="l:\files\cut\log" 
                $sumatra="l:\SumatraPDF.exe"      
                $pdftoprint=""
                $printername= "MainLBL"

    ### Get the List of files in the Directory, print file, wait and then move file
    Get-ChildItem -Path $scandir -filter "*.pdf" -Name | % { 
            $pdftoprint=$_ 
            if($LASTEXITCODE -ne 0) { 
                # Do something
                # Reset so we know when sumatra fails
                $LASTEXITCODE = 0
            }
            & $sumatra -silent $scandir\$pdftoprint -print-to $printername
            if($LASTEXITCODE -ne 0) { 
                # Do something to handle sumatra
            }
            sleep 3 

            # Split up copy and delete so we never loose files
            [system.io.file]::Copy("$scandir\$pdftoprint", "$scanbackdir", $true)
            [system.io.file]::Delete("$scandir\$pdftoprint")
        }
    } 

    ### Define what happens when script fails
    $erroraction = { 
        echo "$(get-date) the process crashed" | Out-File -Append "l:\files\cut\log\errorlog.txt"
    }    

    ### DECIDE WHICH EVENTS SHOULD BE WATCHED 
    $ErrorEvent  = Register-ObjectEvent $watcher "Error" -Action $erroraction
    $CreatedEvent = Register-ObjectEvent $watcher "Created" -Action $action
    $ListOfEvents = @(
        $ErrorEvent  
        $CreatedEvent
    )

    while ($true) {

        $eventMissing = $false
        $ListOfEvents | % { 
            $e = $_

            if (!(Get-Event -SourceIdentifier $e.Name -ErrorAction SilentlyContinue)) {
                # Event does not exist 
                $eventMissing = $true
            }
        }

        if(!$watcher || $eventMissing -eq $true) {
            # deregister events 
            $ListOfEvents | % { 
                $e = $_
                try {
                    Unregister-Event -SourceIdentifier $e.Name
                } catch { 
                    # Do Nothing
                }
            }
            if($watcher) {
                $watcher.Dispose()
                $watcher = $null   
            } else {
                # Create watcher 
                $watcher = New-Object System.IO.FileSystemWatcher
                $watcher.Path = $watcherArgs.Path
                $watcher.Filter = $watcherArgs.Filter
                $watcher.IncludeSubdirectories = $watcherArgs.IncludeSubdirectories
                $watcher.EnableRaisingEvents = $watcherArgs.EnableRaisingEvents
                $ErrorEvent  = Register-ObjectEvent $watcher "Error" -Action $erroraction
                $CreatedEvent = Register-ObjectEvent $watcher "Created" -Action $action
                $ListOfEvents = @(
                    $ErrorEvent  
                    $CreatedEvent
                )
            }

        }

        if ($watcher.EnableRaisingEvents -eq $false) {
            $watcher.EnableRaisingEvents = $watcherArgs.EnableRaisingEvents
        }

        sleep 5
    }

} finally {
    $ListOfEvents | % { 
        $e = $_
        try {
            Unregister-Event -SourceIdentifier $e.Name
        } catch { 
            # Do Nothing
        }
    }
    if($watcher) {
        $watcher.Dispose();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...