powershellwindows-task-scheduler

Ps1 script not executing properly when using Task Scheduler


I am trying to use Task Scheduler to run a PS1 script, however it does not seem to work. When I run the task PowerShell opens briefly, but the script does not run. When I execute the script manually it runs as expected.

Script:

Add-Type -AssemblyName System.Windows.Forms
$Timer = [System.Timers.Timer]::new(3000)
Register-ObjectEvent -InputObject $Timer -EventName Elapsed -Action{
    [System.Windows.Forms.SendKeys]::SendWait("%{TAB}")
}

$Timer.Start()

Is there anything I am missing here?


Solution

  • As in my comment, to execute a script (*.ps1) you just need to call the -File Argument or -f for short however, doing only this, will not result in what you want (a task that is Alt-Tabbing indefinitely?) because the task would end as soon as the ObjectEvent was registered.

    As a workaround, you need to find a way to keep the scheduled task alive, ergo, keep the powershell.exe process running in the background alive.

    I'll list a few option so you can decide which one you like more or fits your need.

    1. The easiest one, in my opinion, is just to add the -NoExit argument to your task, this will keep the task alive indefinitely, until, manually ending it / reboot / shutdown / killing the powershell.exe process / etc.

      • Program: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
      • Arguments: -WindowStyle Hidden -NoExit -f "D:\testing\script.ps1"
    2. Adding a loop on the code, for this you have many options, for example
      Get-EventSubscriber | Wait-Event which, same as before, will keep the task alive indefinitely.

      • Program: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
      • Arguments: -WindowStyle Hidden -f "D:\testing\script.ps1"
      Add-Type -AssemblyName System.Windows.Forms
      
      $Timer = [System.Timers.Timer]::new(3000)
      Register-ObjectEvent -InputObject $Timer -EventName Elapsed -Action {
          [System.Windows.Forms.SendKeys]::SendWait('%{TAB}')
      }
      
      $Timer.Start()
      
      Get-EventSubscriber | Wait-Event
      
    3. A loop that would keep the task alive for X days / hours / minutes:

      • Program: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
      • Arguments: -WindowStyle Hidden -f "D:\testing\script.ps1"
      • Code (you can use a [datetime] object instead of [System.Diagnostics.Stopwatch] here, your call):
      Add-Type -AssemblyName System.Windows.Forms
      
      $Timer = [System.Timers.Timer]::new(3000)
      Register-ObjectEvent -InputObject $Timer -EventName Elapsed -Action {
          [System.Windows.Forms.SendKeys]::SendWait('%{TAB}')
      }
      
      $Timer.Start()
      
      $elapsed = [System.Diagnostics.Stopwatch]::StartNew()
      
      do {
          Start-Sleep -Milliseconds 5
      }
      until($elapsed.Elapsed.Minutes -ge 60)
      
      $elapsed.Stop()
      exit 0
      
    4. Last one, similar to the example before but not using an ObjectEvent at all:

      • Program: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
      • Arguments: -WindowStyle Hidden -f "D:\testing\script.ps1"
      Add-Type -AssemblyName System.Windows.Forms
      
      $elapsed = [System.Diagnostics.Stopwatch]::StartNew()
      
      do {
          [System.Windows.Forms.SendKeys]::SendWait('%{TAB}')
          Start-Sleep -Milliseconds 3000
      }
      until($elapsed.Elapsed.Seconds -ge 30)
      
      $elapsed.Stop()
      exit 0