Search code examples
powershelltimerrunspace

manage objects and variables from another PowerShell runspace


I am currently working with the System.Timers.Timer object in Powershell.

The Problem i have encountered is, that when you register to the "Elapsed" event

Register-ObjectEvent -InputObject $timer -EventName Elapsed -SourceIdentifier ThirtySecTimer -Action $scriptblock

the scriptblock will run in a different Powershell Runspace / thread The problem here is, that i want to modify the timer object in the scriptblock and basically create a loop of shorter Intervals until it reaches x seconds and the script stops

$action={
    $timer.stop()
    Show-MsgBox -Prompt "time's up" #selfdefined function using Windows Forms
    $timer.interval=$interval - 1000
    $timer.start()
}

I have found the option to define runspaces, but i am not sure if i can work with the timer object through a self defined runspace. Also i think using runspaces is a bit over the top for this task.

Is there another (simpler) way to get this to work? If not, is it possible to manipulate the timer object through self defined runspaces? (i probably do this in a different way if i'd have to use runspaces, but it is nice to know for the future)


Solution

  • The $timer itself is passed along as the first argument to the event handler as the sender. This populates the $Sender automatic variable inside the action block automatically.

    You can modify that object reference instead of dereferencing $timer directly:

    # Create a timer starting at a 10 second interval
    $timer = New-Object System.Timers.Timer
    $timer.Interval = 10000
    
    # Register the event handler
    Register-ObjectEvent $timer Elapsed timersourceid -Action {
        Write-Host "Event triggered at $(Get-Date|Select-Object -ExpandProperty TimeOfDay)"
    
        $Sender.Stop()
        if($Sender.Interval -ge 1000)
        {
            $Sender.Interval = $Sender.Interval - 1000
            $Sender.Start()
        }
        else
        {
            Write-Host "Timer stopped"
        }
    }
    

    You can also override the variable names by defining a param block inside the Action scriptblock, first argument is always the sender, second argument is the EventArgs (equivalent to the $EventArgs auto variable):

    Register-ObjectEvent $Timer Elapsed SourceId -Action {
        param($s,$e)
    
        $s.Stop()
        Write-Host "Event was raised at $($e.SignalTime)"
    }