title: How to run a progress bar asynchronously while performing silent installs?
body: <h1>The Goal:</h1>
  
  <p>I want to asynchronously run a progress bar (which shows elapsed time/estimated time) while waiting for silent installs. For instance,</p>
  
  <pre><code>RunWithProgressBar "cmd /c """" /Wait ""Setup.exe""" $(New-Timespan -Minutes 5)
  </code></pre>
  
  <h1>Closest I've Come:</h1>
  
  <pre><code>## Functions
  
  function global:BottleOfBeer {
    $beer = $args[0]
    if ($beer -eq 1) {
      echo "$beer beer on the wall.";
    } elseif ($beer -eq 0) {
      echo "No beer left on the wall!";
    } else {
      echo "$beer beers left on the wall.";
    }
    sleep 1
  }
  
  function global:BeersOnTheWall {
    $NumBeers = $args[0]
    for ($i=$NumBeers; $i -ge 0; $i--) {
      BottleOfBeer $i
    }
  }
  
  function global:Install {
      cmd /c @"
          "AutoHotkey112306_Install.exe" /S /D="%cd%"
  "@
  }
  
  function global:Uninstall {
      cmd /c start "" /wait "Installer.ahk" /Uninstall
  }
  
  ####START Progress Bar Stuff
  function global:DisplayProgress {
    $display = $args[0]
    Write-Progress    -Activity "Running..." -Status "$display"
  }
  
  function global:FormatDisplay {
    $StartTime = $args[0]
    $RunningTime = ($args[1]).Elapsed
    $EstimatedTime = $args[2]
    $RunningTimeDisplay = $([string]::Format("{0:d2}:{1:d2}:{2:d2}",
      $RunningTime.hours, 
      $RunningTime.minutes, 
      $RunningTime.seconds))
    $EstimatedEnd = $StartTime + $EstimatedTime
    return $([string]::Format("(Start: {0}) (Elapsed/Estimated: {1}/{2}) (EstimatedEnd: {3})",
      $StartTime.ToShortTimeString(), 
      $RunningTimeDisplay, 
      $EstimatedTime,
      $EstimatedEnd.ToShortTimeString()))
  }
  
  function global:TearDownProgressBar {
    $job = $args[0]
    $event = $args[1]
    $job,$event | Stop-Job -PassThru | Remove-Job #stop the job and event listener
    Write-Progress -Activity "Working..." -Completed -Status "All done."
  }
  
  function RunWithProgressBar {
    $Payload = $args[0]
    $EstimatedTime = $args[1]
  
    $global:StartTime = Get-Date
    $global:RunningTime = [System.Diagnostics.Stopwatch]::StartNew()
    $global:EstimatedTime = $EstimatedTime
  
    $progressTask = {
      while($true) {
        Register-EngineEvent -SourceIdentifier MyNewMessage -Forward
        $null = New-Event -SourceIdentifier MyNewMessage -MessageData "Pingback from job."
        Start-Sleep -Seconds 1
      }
    }
  
    $job = Start-Job -ScriptBlock $progressTask
    $event = Register-EngineEvent -SourceIdentifier MyNewMessage -Action {
      DisplayProgress $(FormatDisplay $global:StartTime $global:RunningTime $global:EstimatedTime)
    }
  
    try {
      sleep 1
      Invoke-Expression $Payload
    } finally {
      TearDownProgressBar $job $event
    }
  }
  ####END Progress Bar Stuff
  
  ## MAIN
  
  RunWithProgressBar "BeersOnTheWall 2"  $(New-Timespan -Seconds 3)
  RunWithProgressBar "Install"  $(New-Timespan -Seconds 30)
  RunWithProgressBar "Uninstall"  $(New-Timespan -Seconds 5)
  RunWithProgressBar "BeersOnTheWall 2"  $(New-Timespan -Seconds 3)
  </code></pre>
  
  <h1>The Problem</h1>
  
  <p>Although the above implementation runs as intended, whenever the payload argument of <code>RunWithProgressBar</code> is an install the event which updates the progress bar stops getting triggered. </p>
  
  <h1>What I'm Looking For:</h1>
  
  <p>How to modify my current implementation to update the progress bar every second, even while performing an install?</p>