Public/Start-RSRestartFileWatcher.ps1

<#
.SYNOPSIS
Starts a FileSystemWatcher that triggers Restart-RSSession.
 
.DESCRIPTION
Starts a FileSystemWatcher that triggers Restart-RSSession when a specified directory is changed.
Throws a non-terminating error when the caller is not in a restartable session.
 
.PARAMETER Path
Path of the directory to watch. It follows the spec of FileSystemWatcher's Path property.
 
.PARAMETER Filter
Filter string used to determine what files are monitored in a directory. It follows the spec of FileSystemWatcher's Filter property.
The default is '*.*' (Watches all files.) '*.txt' watches the files whose extension is '.txt'.
 
.PARAMETER IncludeSubdirectories
Switch that indicates whether subdirectories of the specified directory should be monitored.
 
.PARAMETER PassThru
The function returns a watcher object if this switch is specified.
 
.INPUTS
None.
 
.OUTPUTS
PSCustomObject if PassThru is specified. An object that represents a watcher.
 
.EXAMPLE
# Monitors all files in a directory including subdirectories.
Start-RSRestartFileWatcher -Path D:\ScriptModuleTest -IncludeSubdirectories
 
.EXAMPLE
# Monitors ps1 and psm1 files in a directory including subdirectories.
Start-RSRestartFileWatcher -Path D:\ScriptModuleTest -Filter '*.ps1' -IncludeSubdirectories
Start-RSRestartFileWatcher -Path D:\ScriptModuleTest -Filter '*.psm1' -IncludeSubdirectories
 
.EXAMPLE
# Monitors all files in a directory and stores the watcher object to stop it later.
$watcher = Start-RSRestartFileWatcher -Path D:\ScriptModuleTest -PassThru
 
.LINK
https://learn.microsoft.com/en-us/dotnet/api/system.io.filesystemwatcher?view=net-7.0
 
#>

function Start-RSRestartFileWatcher
{
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    param
    (
        [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
        [String]$Path,

        [Parameter(ValueFromPipelineByPropertyName=$true)]
        [String]$Filter,

        [Parameter(ValueFromPipelineByPropertyName=$true)]
        [Switch]$IncludeSubdirectories,

        [Parameter(ValueFromPipelineByPropertyName=$true)]
        [Switch]$PassThru
    )

    process
    {
        if (-not [RestartableSession.GlobalVariable]::IsInRestartableSession())
        {
            Write-Error -Message 'Not in a RS Session.' -Category InvalidOperation
            return
        }

        $fileSystemWatcher = New-Object System.IO.FileSystemWatcher
        $fileSystemWatcher.Path = $Path
        $fileSystemWatcher.Filter = $Filter
        $fileSystemWatcher.IncludeSubdirectories = $IncludeSubdirectories
        $fileSystemWatcher.EnableRaisingEvents = $true
        $fileSystemWatcher.NotifyFilter = 'FileName, DirectoryName, LastWrite'

        $action = {
            $process = Get-Process -Id $Event.MessageData -ErrorAction Ignore
            if ($process)
            {
                if ([RestartableSession.GlobalVariable]::OnEnd)
                {
                    [RestartableSession.GlobalVariable]::OnEnd.Invoke([RestartableSession.GlobalVariable]::OnEndArgumentList) | Out-Default
                }
                Stop-Process $process
            }
        }

        $watcher = [PSCustomObject]@{
            FileSystemWatcher = $fileSystemWatcher
            Created = Register-ObjectEvent $fileSystemWatcher 'Created' -Action $action -MessageData $PID
            Changed = Register-ObjectEvent $fileSystemWatcher 'Changed' -Action $action -MessageData $PID
            Deleted = Register-ObjectEvent $fileSystemWatcher 'Deleted' -Action $action -MessageData $PID
            Renamed = Register-ObjectEvent $fileSystemWatcher 'Renamed' -Action $action -MessageData $PID
        }

        if ($PassThru)
        {
            $watcher
        }
    }
}