Private/Invoke-Executable.ps1

# Copyright: (c) 2018, Jordan Borean (@jborean93) <jborean93@gmail.com>
# MIT License (see LICENSE or https://opensource.org/licenses/MIT)

Function Invoke-Executable {
    param(
        [Parameter(Mandatory=$true)][String]$Executable,
        $Arguments,
        [Switch]$GetOutput
    )
    Write-Verbose -Message "Starting executable '$Executable' aith the arguments '$Arguments'"

    $process = New-Object -TypeName System.Diagnostics.Process
    $process.StartInfo.FileName = $Executable
    $process.StartInfo.Arguments = $Arguments
    $process.StartInfo.UseShellExecute = $false

    $process.StartInfo.RedirectStandardError = $true
    $process.StartInfo.RedirectStandardOutput = $true

    Add-Type -TypeDefinition @'
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Management.Automation.Host;
 
namespace Command
{
    public static class ProcessUtil
    {
        public static void GetProcessOutput(StreamReader stdoutStream, StreamReader stderrStream, out string stdout, out string stderr)
        {
            EventWaitHandle sowait = new EventWaitHandle(false, EventResetMode.ManualReset);
            EventWaitHandle sewait = new EventWaitHandle(false, EventResetMode.ManualReset);
            string so = null;
            string se = null;
            ThreadPool.QueueUserWorkItem((s)=>
            {
                so = stdoutStream.ReadToEnd();
                sowait.Set();
            });
            ThreadPool.QueueUserWorkItem((s) =>
            {
                se = stderrStream.ReadToEnd();
                sewait.Set();
            });
            foreach(WaitHandle wh in new WaitHandle[] { sowait, sewait })
                wh.WaitOne();
            stdout = so;
            stderr = se;
        }
 
        public static void WriteProcessOutput(StreamReader stdoutStream, StreamReader stderrStream, PSHostUserInterface hostUI)
        {
            EventWaitHandle sowait = new EventWaitHandle(false, EventResetMode.ManualReset);
            EventWaitHandle sewait = new EventWaitHandle(false, EventResetMode.ManualReset);
            ThreadPool.QueueUserWorkItem((s)=>
            {
                while (!stdoutStream.EndOfStream)
                    hostUI.WriteLine(stdoutStream.ReadLine());
                sowait.Set();
            });
            ThreadPool.QueueUserWorkItem((s) =>
            {
                while (!stderrStream.EndOfStream)
                    hostUI.WriteErrorLine(stderrStream.ReadLine());
                sewait.Set();
            });
            foreach(WaitHandle wh in new WaitHandle[] { sowait, sewait })
                wh.WaitOne();
        }
    }
}
'@


    $process.Start() > $null
    if ($GetOutput) {
        $stdout = $stderr = $null
        [Command.ProcessUtil]::GetProcessOutput($process.StandardOutput, $process.StandardError, [ref]$stdout, [ref]$stderr)
        $process.WaitForExit() > $null
        return $stdout, $stderr, $process.ExitCode
    } else {
        # AppVeyor console does not have a proper ConsoleHost, we need to use
        # the PSHostUserInterface methods to write the child process' stdout
        # and stderr
        [Command.ProcessUtil]::WriteProcessOutput($process.StandardOutput, $process.StandardError, $Host.UI)
        $process.WaitForExit() > $null
        return $process.ExitCode
    }
}