title: Cancellable remote processes and capturing output
body: <p>I'm trying to use PowerShell via its C# API to do the following:</p>
  
  <ol>
  <li>start remote processes</li>
  <li>terminate them</li>
  <li>capture standard and error output of remote processes (not after the process terminates, but as that output is being produced).</li>
  <li>capture the exit code of remote processes.</li>
  </ol>
  
  <p>I'm starting remote processes with the following PowerShell script:</p>
  
  <pre><code>$ps = new-object System.Diagnostics.Process
  $ps
  $ps.StartInfo.Filename = "c:\Echo.exe"
  $ps.StartInfo.Arguments = ""
  $ps.StartInfo.RedirectStandardOutput = $true
  $ps.StartInfo.RedirectStandardError = $true
  $ps.StartInfo.UseShellExecute = $false
  $ps.start()
  $ps.WaitForExit()
  $ps.ExitCode
  </code></pre>
  
  <p>The C# part of my prototype looks like the following:</p>
  
  <pre><code>// Note: in this example the process is started locally
  using (Runspace runspace = RunspaceFactory.CreateRunspace(/*_remotePcConnectionInfo*/))
  {
      runspace.Open();
  
      Pipeline pipeline = runspace.CreatePipeline();
      // Scripts.RunExecutable is that script above
      pipeline.Commands.AddScript(Scripts.RunExecutable);
  
      pipeline.InvokeAsync();
  
      var process = (Process)pipeline.Output.Read().BaseObject;
  
      bool started = (bool)pipeline.Output.Read().BaseObject;
  
      // Not showing the dummy event handlers - they simply do a Console.WriteLine now.
      process.OutputDataReceived += new DataReceivedEventHandler(process_OutputDataReceived);
      process.BeginOutputReadLine();
  
      // Not showing the dummy event handlers - they simply do a Console.WriteLine now.
      process.ErrorDataReceived += new DataReceivedEventHandler(process_ErrorDataReceived);
      process.BeginErrorReadLine();
  
      int processExitCode = (int) pipeline.Output.Read().BaseObject;
  }
  </code></pre>
  
  <p>This does capture the output of processes run locally, but <b>will this work for remote processes as well?</b> (Another, less important question is: how can this work for remote processes? Is .Net Remoting involved in some way and am I getting a proxy for Process?) <b>If not, what's the way to do that? Do mind that I need the output as it's being produced, not after the process is terminated</b>. </p>
  
  <p>This does not capture process termination. I've tried doing termination by capturing process Id first and then running "Stop Process " PowerShell script from a different runspace. That failed, because "the pipeline is already running" and pipelines cannot run in parallel... Then I've tried invoking process.Kill() from C#, <b>that worked for my local process, but SO reports it won't work for remote processes...</b> Then I've tried tuning my PowerShell script so it included a global variable and a waiting loop, but I never figured out how to set that variable after the pipeline is started. The added loop looked like this:</p>
  
  <pre><code>while ($ps.HasExited -eq $false -and $global:cancelled -eq $false)
  {
      Start-Sleep -seconds 1
  }
  
  if ($global:cancelled -eq $true)
  {
      $ps.Kill()
  }
  </code></pre>
  
  <p>So, that failed, too. <b>Any advice on process termination for my scenario?</b></p>
  
  <p><b>Is PowerShell even a good fit for this?</b> (we're strongly against openSSH we've tried using before because of its issues with forking).</p>
  
  <p><b>UPDATE:</b> What I've ended up doing was calling (via C# starting a process -- "powershell" + "-Command ...") into powershell.exe that was told to execute a script (invoke-command) on a remote computer. Powershell captures output produced on remote PC by default, so I could easily get this of the Process instance. When I wanted to cancel, I killed the local process. The return code of a remote command was trickier. I will post the command, script and C#-snippet in a while.</p>