Process.Utils.ps1

function Start-ProcessExt
(
     [parameter(Mandatory=$true, Position=0)] [String] $exePath,
     [parameter(Mandatory=$false, Position=1)] [String[]] $params,
     [parameter(Mandatory=$false, Position=2)] [String] $workingDirectory = $null
) 
{
    [OutputType([System.Int32])]

    $arguments = ToArguments $params
    $startInfo = New-Object 'System.Diagnostics.ProcessStartInfo' -Property @{
        FileName               = $exePath;
        Arguments              = $arguments;
        UseShellExecute        = $false;
        CreateNoWindow         = $true;
        RedirectStandardOutput = $true;
        StandardOutputEncoding = [Text.Encoding]::UTF8;
        RedirectStandardError  = $true;
        WorkingDirectory       = $workingDirectory;
    }

    Write-Verbose "$exePath $arguments (in $(Get-Not-Null-Or-Empty $workingDirectory $([System.Environment]::CurrentDirectory)))"
    
    $process = [Diagnostics.Process]::Start($startInfo)

    while (!$process.StandardOutput.EndOfStream) {

        [string]$line = $process.StandardOutput.ReadLine()

        if ($line.StartsWith('warning:', [System.StringComparison]::InvariantCultureIgnoreCase)) {
            Write-Warning $line
        }
        elseif ($line.StartsWith('verbose:', [System.StringComparison]::InvariantCultureIgnoreCase)) {
            Write-Verbose $line
        }
        else {
            Write-Host $line
        }
    }

    $process.WaitForExit()


    if($process.ExitCode) {
        [System.Text.StringBuilder]$error = New-Object 'System.Text.StringBuilder'

        $error.AppendLine("Process execution failed:") | Out-Null
        $error.AppendLine("$exePath $arguments (in $(Get-Not-Null-Or-Empty $workingDirectory $([System.Environment]::CurrentDirectory)))") | Out-Null
    
        if ($process.StandardError) {
            while (!$process.StandardError.EndOfStream) {
                $error.AppendLine($process.StandardError.ReadLine()) | Out-Null
            }
        }

        Write-Error $error.ToString()
    }
    
    return $process.ExitCode
}

function Get-Not-Null-Or-Empty ([String]$param1, [String]$param2) {
    if (-not [System.String]::IsNullOrEmpty($param1)) {
        return $param1
    }

    if (-not [System.String]::IsNullOrEmpty($param2)) {
        return $param2
    }

    throw 'Provided parameters are null or empty'
}

function ToArguments($params) {
    $arguments = ''
    for ($i = 0; $i -lt $params.Length; $i++) {
        if ($i) {
            $arguments += ' '
        }

        if (!$params[$i].Contains(' ')) {
            $arguments += $params[$i]

            continue
        }

        $arguments += '"'

        $pendingBackslashs = 0
        for ($j = 0; $j -lt $params[$i].Length; $j++) {
            switch ($params[$i][$j]) {
                '"' {
                    if ($pendingBackslashs) {
                        $arguments += '\' * $pendingBackslashs * 2
                        $pendingBackslashs = 0
                    }
                    $arguments += '\"'
                }

                '\' {
                    $pendingBackslashs++
                }

                default {
                    if ($pendingBackslashs) {
                        if ($pendingBackslashs -eq 1) {
                            $arguments += '\'
                        }
                        else {
                            $arguments += '\' * $pendingBackslashs * 2
                        }

                        $pendingBackslashs = 0
                    }

                    $arguments += $params[$i][$j]
                }
            }
        }

        if ($pendingBackslashs) {
            $arguments += '\' * $pendingBackslashs * 2
        }

        $arguments += '"'
    }

    return $arguments
}