2

I have a console application and a method that executes a PowerShell script within the console application. So I'm trying to grab an error text that it outputs in the application and do something with it.

Example/What I'm trying to do:

    If Error.contains("Object") 
{
    // do something here
}

Here is my current method

  public void ExecutePowershellScript()
{
  var file = @"C:\Path\filename.ps1";
           
            var start = new ProcessStartInfo()
            {
                FileName = "powershell.exe",
                Arguments = $"-NoProfile -ExecutionPolicy unrestricted -file \"{file}\"",
                UseShellExecute = false
            };
            Process.Start(start);
}
Broski
  • 75
  • 9

3 Answers3

3

Process.start: how to get the output?

When you create your Process object set StartInfo appropriately:

var proc = new Process 
{
    StartInfo = new ProcessStartInfo
    {
        FileName = "program.exe",
        Arguments = "command line arguments to your executable",
        UseShellExecute = false,
        RedirectStandardOutput = true,
        CreateNoWindow = true
    }
};

then start the process and read from it:

proc.Start();
while (!proc.StandardOutput.EndOfStream)
{
    string line = proc.StandardOutput.ReadLine();
    // do something with line
}

You can use int.Parse() or int.TryParse() to convert the strings to numeric values. You may have to do some string manipulation first if there are invalid numeric characters in the strings you read.

  • 1
    Yes, and you can set async error handlers `process.OutputDataReceived += new DataReceivedEventHandler(OutputHandler); process.ErrorDataReceived += new DataReceivedEventHandler(OutputHandler);` //* Start process and handlers – Jason N Aug 04 '21 at 04:58
2

You can set RedirectStandardError = true and access any errors from process.StandardError

    public static void ExecutePowershellScript()
    {
        var file = @"C:\Path\filename.ps1";

        var start = new ProcessStartInfo()
        {
            FileName = "powershell.exe",
            Arguments = $"-NoProfile -ExecutionPolicy unrestricted -file \"{file}\"",
            UseShellExecute = false,
            RedirectStandardOutput = true,
            RedirectStandardError = true
        };
        using Process process = Process.Start(start);
        string output = process.StandardOutput.ReadToEnd();
        string errors = process.StandardError.ReadToEnd();
    }



Okay, scratch the above suggestion. After being corrected by mklement0,

This is a perfectly reasonable attempt, but, unfortunately, it can lead to hangs (while waiting for one's stream end, the other, when exceeding the buffer size, may cause process execution to block). If you need to capture both streams, you must collect the output from one of them via events. – mklement0

I changed the solution to use the ErrorDataReceived event

        public static async Task ExecutePowershellScript()
        {
            var file = @"C:\Path\filename.ps1";

            var start = new ProcessStartInfo
            {
                FileName = "powershell.exe",
                Arguments = $"-NoProfile -ExecutionPolicy unrestricted -file \"{file}\"",
                UseShellExecute = false,
                // redirect standard error stream to process.StandardError
                RedirectStandardError = true
            };

            using var process = new Process
            {
                StartInfo = start
            };

            // Subscribe to ErrorDataReceived event
            process.ErrorDataReceived += (sender, e) =>
            {
                //  code to process the error lines in e.Data
            };

            process.Start();

            // Necessary to start redirecting errors to StandardError
            process.BeginErrorReadLine();

            // Wait for process to exit
            await process.WaitForExitAsync();

        }
Daniel
  • 4,792
  • 2
  • 7
  • 20
  • 1
    This is a perfectly reasonable attempt, but, unfortunately, it can lead to hangs (while waiting for one's stream end, the other, when exceeding the buffer size, may cause process execution to block). If you need to capture _both_ streams, you must collect the output from one of them _via events_. – mklement0 Aug 04 '21 at 02:09
  • 2
    Fair enough. Thanks for the info! @mklement0 – Daniel Aug 04 '21 at 02:33
  • @mklement0, ever think of making a book (*or article/blog*) bridging the gap between *C#* and PowerShell? *I'm real persistent on these book ideas* lol – Abraham Zinala Aug 04 '21 at 04:50
  • Thanks for updating, @Daniel. Just to reiterate: The original approach works robustly when only _one_ stream is captured. When _both_ need to be captured, the event-based approach is the only fully robust one, though, pragmatically speaking, reading from both streams in a _loop_, _line by line_, will work too - you would only get in trouble with excessively long individual lines (close to 64KiB and above). – mklement0 Aug 04 '21 at 22:40
  • 1
    @AbrahamZinala :) I'm starting to get concerned about the amount of homework... – mklement0 Aug 04 '21 at 22:41
  • P.S, @Daniel, a quibble: you don't strictly need the `async` approach for the event-based approach. – mklement0 Aug 04 '21 at 22:42
0
start.Start();
while (!start.StandardOutput.EndOfStream)
{
    string line = start.StandardOutput.ReadLine();
}