Powershell "hanging" due to FileSystemWatcher

Hi all

Please forgive me for possibly asking a stupid or repeated question, I’m only really a notice scripter and an absolute newbie when it comes to PowerShell

We have a convoluted solution to some printing issues (caused by citrix and remote servers). Basically from the main server, we force push a pdf file to the remote pc and then have a powershell script which constantly runs on the remote pc to “catch” the file and push it to the local printer

This works “fine”

However we get random dropouts. The powershell script doesn’t seem to have crashed because it’s still running in windows but the actual action doesn’t seem to be processing new files

I have done A LOT of reading today and there’s mention of having to name and unregister events when you’re done otherwise it can cause a buffer overflow issues and make powershell stop processing the action. But I’m unsure where it should actually go within the code. The idea is that this script will run permanently, so do we unregister or remove the event within the action itself or somewhere else?

I previous had A LOT of dummy logging going on within the action to try to find where it failed, but it seems to stop at different points without any justifiable reason (ie, it would fail at the command to find files, other times at the command to move etc etc)

### 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

Are there a lot of documents being processed? Have you already tried increasing the buffer size? InternalBufferSize

cross posted. Here was my response to you from the other site.

If you want a script to run in the background, then look to PowerShell background jobs.

If you want a script to run permanently, then you want to make it a service …

See these:

How to Create a User-Defined Service

How to run a PowerShell script as a Windows service

… or attach that to a scheduled task, that would restart it on reboots.

There are two ways to implement a FileSystemWatcher.

  • Synchronous
  • Asynchronous

A synchronous FileSystemWatcher, by it nature, when a change is detected, control is returned to your script so it can process the change. If another file change occurs while your script is no longer waiting for events, it gets lost. Hence leading to unexpected outcomes.

Using the FileSystemWatcher Asynchronously, it would continue to log new filesystem changes and process them once PowerShell is done processing previous changes.

*** An Sample - Example Asynchronous 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.'
}