15

I am using System.Management.Automation.Runspaces to execute PowerShell scripts. Is there an option I can read the exit code of a given script?

using System.IO;
using System.Management.Automation.Runspaces;
using System.Collections.ObjectModel;
using System.Management.Automation;

namespace PowerShell
{
    public class PowerShellExecuter
    {
        public Collection<PSObject> RunPsScript(string psScriptFile)
        {
            string psScript;
            if (File.Exists(psScriptFile))
            {
                psScript = File.ReadAllText(psScriptFile);
            }
            else
            {
                throw new FileNotFoundException("Wrong path for the script file");
            }
            Runspace runSpace = RunspaceFactory.CreateRunspace();
            runSpace.Open();

            RunspaceInvoke runSpaceInvoker = new RunspaceInvoke(runSpace);
            runSpaceInvoker.Invoke("Set-ExecutionPolicy Unrestricted");

            Pipeline pipeLine = runSpace.CreatePipeline();
            pipeLine.Commands.AddScript(psScript);
            pipeLine.Commands.Add("Out-String");

            Collection<PSObject> returnObjects = pipeLine.Invoke();
            runSpace.Close();

            return returnObjects;
        }
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user829174
  • 6,132
  • 24
  • 75
  • 125

3 Answers3

7

PowerShell commands have a richer error mechanism than integer exit codes. There is an error stream that non-terminating errors appear on. Terminating errors result in thrown exceptions so you need to handle those. The following code shows how to use the two mechanisms:

using System;
using System.Collections.ObjectModel;
using System.Management.Automation;

namespace PowerShellRunspaceErrors
{
    class Program
    {
        private static PowerShell s_ps;

        static void Main(string[] args)
        {
            s_ps = PowerShell.Create();
            ExecuteScript(@"Get-ChildItem c:\xyzzy");
            ExecuteScript("throw 'Oops, I did it again.'");
        }

        static void ExecuteScript(string script)
        {
            try
            {
                s_ps.AddScript(script);
                Collection<PSObject> results = s_ps.Invoke();
                Console.WriteLine("Output:");
                foreach (var psObject in results)
                {
                    Console.WriteLine(psObject);
                }
                Console.WriteLine("Non-terminating errors:");
                foreach (ErrorRecord err in s_ps.Streams.Error)
                {
                    Console.WriteLine(err.ToString());
                }
            }
            catch (RuntimeException ex)
            {
                Console.WriteLine("Terminating error:");
                Console.WriteLine(ex.Message);
            }
        }
    }
}

If you run this program it outputs:

Output:
Non-terminating errors:
Cannot find path 'C:\xyzzy' because it does not exist.
Terminating error:
Oops, I did it again.
Press any key to continue . . .
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Keith Hill
  • 194,368
  • 42
  • 353
  • 369
0

Processes return exit codes, not PowerShell Runspaces.

As far as I know, the best way to capture the state of a Runspace would be to subscribe to the Runspace.StateChanged event, which will give you RunspaceStateInfo to deal with. There are two useful properties on RunspaceStateInfo: Reason, and State.

http://msdn.microsoft.com/en-us/library/system.management.automation.runspaces.runspacestateinfo_members(v=vs.85).aspx

0

It's as simple as asking the value to the runspace you just created:

s_ps.AddScript("$LASTEXITCODE");
results = s_ps.Invoke();
int.TryParse(results[0].ToString(),out valorSortida);
Garfius
  • 39
  • 8
  • This put me on the right track. HadErrors and Streams.Error doesn't mean diddly squad, it totally depends on how the script is dealing with the error stream (if at all) and whether it handles exceptions or not. I now clear the output collection and execute another one-liner to output a variable I know to hold the result of my script (the way you demonstrate). The only reliable way I have found. I would rather have a way to obtain the argument provided with the Exit command that ends the script but this works too. Thanks! – Martin Maat May 13 '18 at 16:00
  • 2
    This does not work, at least in the way I interpret it, as this answer is vague or incomplete. Can you please either provide a full example or explain how this is meant to be used? – Florian Winter Jun 24 '19 at 12:39