title: Captured Output of command run by PowerShell Is Sometimes Incomplete
body: <p>I'm running the DTEXEC.exe command from within a PowerShell script, trying to capture and log the output to a file. Sometimes the output is incomplete and I'm trying to figure out why this the case and what might be done about it. The lines that never seem to get logged are the most interesting:</p>
  
  <pre><code>DTEXEC: The package execution returned DTSER_SUCCESS(0)
  Started:  10:58:43 a.m.
  Finished: 10:59:24 a.m.
  Elapsed:  41.484 seconds
  </code></pre>
  
  <p>The output always seems incomplete on packages that execute in less than ~ 8 seconds and this might be a clue (there isn't much output or they finish quickly).</p>
  
  <p>I'm using .NETs System.Diagnostics.Process and ProcessStartInfo to setup and run the command, and I'm redirecting stdout and stderror to event handlers that each append to a StringBuilder which is subsequently written to disk.</p>
  
  <p>The problem feels like a timing issue or a buffering issue. To solve the timing issue, I've attempted to use Monitor.Enter/Exit. If it's a buffering issue, I'm not sure how to force the Process to not buffer stdout and stderror.</p>
  
  <p>The environment is
  - PowerShell 2 running CLR version 2
  - SQL 2008 32-bit DTEXEC.exe
  - Host Operating System: XP Service Pack 3.</p>
  
  <p>Here's the code:</p>
  
  <pre><code>function Execute-SSIS-Package
  {
      param([String]$fileName)
  
      $cmd = GetDTExecPath
  
      $proc = New-Object System.Diagnostics.Process
      $proc.StartInfo.FileName = $cmd
      $proc.StartInfo.Arguments = "/FILE ""$fileName"" /CHECKPOINTING OFF /REPORTING ""EWP"""
      $proc.StartInfo.RedirectStandardOutput = $True
      $proc.StartInfo.RedirectStandardError  = $True
      $proc.StartInfo.WorkingDirectory = Get-Location
      $proc.StartInfo.UseShellExecute = $False
      $proc.StartInfo.CreateNoWindow = $False
  
      Write-Host $proc.StartInfo.FileName $proc.StartInfo.Arguments
  
      $cmdOut = New-Object System.Text.StringBuilder
  
      $errorEvent = Register-ObjectEvent -InputObj $proc `
          -Event "ErrorDataReceived" `
          -MessageData $cmdOut `
          -Action `
          {
              param
              (
                  [System.Object] $sender,
                  [System.Diagnostics.DataReceivedEventArgs] $e
              )
  
              try
              {
                  [System.Threading.Monitor]::Enter($Event.MessageData)
                  Write-Host -ForegroundColor "DarkRed" $e.Data
                  [void](($Event.MessageData).AppendLine($e.Data))
              }
              catch
              {
                  Write-Host -ForegroundColor "Red" "Error capturing processes std error" $Error
              }
              finally
              {
                  [System.Threading.Monitor]::Exit($Event.MessageData)
              }
          }
  
      $outEvent = Register-ObjectEvent -InputObj $proc `
          -Event "OutputDataReceived" `
          -MessageData $cmdOut `
          -Action `
          {
              param
              (
                  [System.Object] $sender,
                  [System.Diagnostics.DataReceivedEventArgs] $e
              )
              try
              {
                  [System.Threading.Monitor]::Enter($Event.MessageData)
                  #Write-Host $e.Data
                  [void](($Event.MessageData).AppendLine($e.Data))
              }
              catch
              {
                  Write-Host -ForegroundColor "Red" "Error capturing processes std output" $Error
              }
              finally
              {
                  [System.Threading.Monitor]::Exit($Event.MessageData)
              }
          }
  
      $isStarted = $proc.Start()
  
      $proc.BeginOutputReadLine()
      $proc.BeginErrorReadLine()
  
      while (!$proc.HasExited)
      {
          Start-Sleep -Milliseconds 100
      }
  
      Start-Sleep -Milliseconds 1000
  
      $procExitCode = $proc.ExitCode
      $procStartTime = $proc.StartTime
      $procFinishTime = Get-Date
  
      $proc.Close()
  
      $proc.CancelOutputRead()
      $proc.CancelErrorRead()
  
      $result = New-Object PsObject -Property @{
          ExitCode = $procExitCode
          StartTime = $procStartTime
          FinishTime = $procFinishTime
          ElapsedTime = $procFinishTime.Subtract($procStartTime)
          StdErr = ""
          StdOut = $cmdOut.ToString()
      }
  
      return $result
  }
  </code></pre>