7

I'm actually trying to run a timer (lmd) every 50 sec once. but when i press ctrl+c the timer still continues to print the message in its action block. So i wrote an other timer (ctrlc) which runs every sec and looks for any ctrlc pressed. Then unregisters the lmd timer and ctrl timer both. and resets the TreatControlCAsInput to false. But i have a problem inside the ctrlc timer action block. i'm not able to trigger the RemoveTimer function. Please suggest :)

# Disables the timer and unregisters the event subscriber
function RemoveTimer ($timerin, $sourceidentifier) {
     Write-Host "Inside RemoveTimer $sourceidentifier"
    try {
        $timerin.Enabled = $False
        Unregister-Event -SourceIdentifier $sourceidentifier
    } Catch {
    Write-Host "Error occurred while stopping timer $($_.Exception.Message)"
}
}

# Disables the timer and unregisters the event subscriber
function RemoveTimer1 {
    Write-Host "Inside RemoveTimer1"
}

# LMD Timer
try {
    $lmdtimer = New-Object Timers.Timer
    $lmdtimer.Interval = 50000
    $registerevent = Register-ObjectEvent -InputObject $lmdtimer -EventName Elapsed -SourceIdentifier LMDTimer.Output -Action {
        Write-Debug "$($Event | Out-String)"
        Write-Host "Script is running"
    }
    $lmdtimer.Enabled = $True
    Write-Debug "$($lmdtimer | Out-String)"
} Catch {
    Write-Host "Error occurred while starting LMDTimer $($_.Exception.Message)"
    RemoveTimer $lmdtimer "LMDTimer.Output"
}

# CTRLC Timer
try {
    [console]::TreatControlCAsInput = $true
    $ctrlctimer = New-Object Timers.Timer
    $ctrlctimer.Interval = 1000
    $params = new-object psobject -property @{RemoveTimer1 = $function:RemoveTimer1}
    $registerevent = Register-ObjectEvent -InputObject $ctrlctimer -EventName Elapsed -SourceIdentifier CTRLCTimer.Output -MessageData $function:RemoveTimer1 -Action {
        if ($Host.UI.RawUI.KeyAvailable -and (3 -eq [int]$Host.UI.RawUI.ReadKey("AllowCtrlC,IncludeKeyUp,NoEcho").Character))
        {
          Write-Host "Removing LMDTimer and CTRLCTimer"
          try {
              #RemoveTimer $lmdtimer "LMDTimer.Output"
              #RemoveTimer $ctrlctimer "CTRLCTimer.Output"
              $Event.MessageData.RemoveTimer1
          } Catch {
              Write-Host $_.Exception.Message
          }
          Write-Host "Removing TreatControlCAsInput"
          [console]::TreatControlCAsInput = $false
          exit
        }
    }
    $ctrlctimer.Enabled = $True
    Write-Debug "$($ctrlctimer | Out-String)"
} Catch {
    Write-Host "Error occurred while starting CTRLCTimer $($_.Exception.Message)"
    RemoveTimer $ctrlctimer "CTRLCTimer.Output"
    [console]::TreatControlCAsInput = $false
}

for ($i=0;$i -lt 4000;$i++) {
    Write-Host "$i"
}

RemoveTimer $lmdtimer "LMDTimer.Output"
RemoveTimer $ctrlctimer "CTRLCTimer.Output"
[console]::TreatControlCAsInput = $false
1
  • your title does not match the question, also you cant "pass" a function. I´m not even sure you can call a function in the scope you are registering the event from (i´d guess you cant). maybe creating a module with your function and importing that during the event can be an alternative, or just embed the functionality inside the scriptblock Commented May 21, 2015 at 16:22

2 Answers 2

12

You can use the -MessageData parameter to pass info to the scriptblock:

$pso = new-object psobject -property @{foo = $foo; bar = $bar}
Register-ObjectEvent... -messagedata $pso

After that you should be able to access it inside the Scriptblock like this:

$Event.MessageData.foo
Sign up to request clarification or add additional context in comments.

3 Comments

Sigh, it is pretty annoying that different powershell commands that take script blocks have different methods of passing parameters to them. Compare with Start-Job, Invoke-Command etc. Anyone know a rational explanation to this?
Thanks btw, this saved the script of the day for me.
Thank you for this answer. I've been scouring documentation, experimenting, searching and testing for 4+ hours.
0

I just ran into almost the same issue today too, note that the scope of the function RemoveTimer1 is too restrictive to be used within the event's -Action script block. The two workarounds I was able to find were:

  1. Dot source the script when you call it
    • PowerShell.exe -File yourScript.ps1
    • . C:\code\yourScript.ps1
  2. Add the function to the global scope, and then delete it when you're done with it function global:RemoveTimer1 { Write-Host "Inside RemoveTimer1" } .... rm function:\RemoveTimer1

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.