Home > Enterprise >  How do you automatically clean up all event subscriptions made in a PowerShell script when that scri
How do you automatically clean up all event subscriptions made in a PowerShell script when that scri

Time:04-01

I'm setting up a FileSystemWatcher to watch a file for changes. This works. I also want to keep the script that sets this up running until the user manually terminates the script. This also works. However, I'd also like the event subscription on the FileSystemWatcher to get automatically cleaned up when the script exits (either normally or abnormally). This doesn't work, because event subscriptions are part of the session, and the script doesn't have its own session.

I tried creating a new session object inside the script and using it for the watcher setup and event registration, which seemed to do a great job cleaning up the event subscription on script termination, but it also seemed to cause all my console activity to get swallowed up in that child session.

How can I make it so that whenever the script exits (normally or abnormally), the event subscription is cleaned up automatically? (And doing this while maintaining visibility of my console output.)

In case the context matters, this is a simple ZIP file build script. I'm trying to add a "watch mode" to it so that when the ZIP is updated by another app, the ZIP is decompressed back to the folder from which it was created. So this script is meant to be executed from a PowerShell command line that remains active and is possibly used for other things before and after this script runs. In other words, the mighty hammer of Get-EventSubscriber | Unregister-Event is potentially a little too mighty, in addition to being another command that the script user would have to invoke on their own.

This is a condensed version of my script:

$watcher = New-Object System.IO.FileSystemWatcher ".", $fileName -Property @{
    NotifyFilter = [IO.NotifyFilters]::LastWrite
}

Register-ObjectEvent $watcher -EventName Changed -Action {
    Write-Host "File change detected."
    # other things, scripts, etc
}

$watcher.EnableRaisingEvents = $true

Write-Host "Press Ctrl C to stop watching the file."

while ($true)
{
    if ([Console]::KeyAvailable)
    {
        $keyInfo = [Console]::ReadKey($true)
        if ($keyInfo.Modifiers -eq [ConsoleModifiers]::Control -and $keyInfo.Key -eq [ConsoleKey]::C)
        {
            Exit
        }
    }
    else
    {
        Start-Sleep 0.5
    }
}

CodePudding user response:

Try the following:

$watcher = New-Object System.IO.FileSystemWatcher $pwd, $fileName -Property @{
  NotifyFilter = [IO.NotifyFilters]::LastWrite
}

# Register with a self-chosen source identifier.
$evtJob = Register-ObjectEvent -SourceIdentifier fileWatcher $watcher -EventName Changed -Action {
  Write-Host "File change detected."
  # other things, scripts, etc
}

$watcher.EnableRaisingEvents = $true

Write-Host "Press Ctrl C to stop watching the file."

try {
  # This blocks execution indefinitely while allowing
  # events to be processed in the -Action script block.
  # Ctrl-C aborts the script by default, which will execute
  # the `finally` block.
  Wait-Event -SourceIdentifier fileWatcher
}
finally {
  # Clean up the event job, and with it the event subscription.
  # Note: If the -Action script block produces output, you
  #       can collect it with Receive-Job first.
  $evtJob | Remove-Job -Force
}
  • Related