powershellpowershell-corerunspace

Why does Write-Host break EndInvoke() result collection from RunspacePool?


If I invoke a simple script, such as 'childScript.ps1':

[CmdletBinding()]
param (
    [Parameter(ValueFromPipeline=$true)]
    $object
)

Write-Host $object

Using BeginInvoke, and ending with EndIvoke(), the write-host output is not available in the result.

E.g:

$childScript = get-content ".\childScript.ps1"
$myObject = "Example string"
$MaxThreads = 5
$RunspacePool = [runspacefactory]::CreateRunspacePool(1, $MaxThreads)
$RunspacePool.Open()
$asyncJob = [PowerShell]::Create().AddScript($childScript).AddParameter('object', $myObject)
$asyncJob.RunspacePool = $HostRunspacePool
$asyncJob.BeginInvoke()
$result = $asyncJob.EndInvoke($job.AsyncHandle) #will be empty

However, if i modify the childScript to be:

[CmdletBinding()]
param (
    [Parameter(ValueFromPipeline=$true)]
    $object
)

Write-Output $object

The result would be "Example string".

If i mix write-host and write-output, the result would always be empty. Is there good documentation on why this occurs?

How can i force only specific output to be captured in the result, even if we use write-xxx in addition to write-output?


Solution

  • Using BeginInvoke(), and ending with EndInvoke(), the Write-Host output is not available in the result.

    That is correct, because EndInvoke() only returns the actual output from your runspace:

    The output buffer created to hold the results of the asynchronous invoke, or null if the caller provided their own buffer.


    Side note, by "if the caller provided their own buffer", they mean if you're using one of the *Invoke<TInput, TOutput>(...) overloads, i.e.:

    $mybuffer = [System.Management.Automation.PSDataCollection[psobject]]::new()
    $ps = [powershell]::Create().AddScript({ 'hello' })
    $ps.EndInvoke($ps.BeginInvoke(
        [System.Management.Automation.PSDataCollection[psobject]] $null,
        $mybuffer)) # No output from here
    
    $mybuffer # Output is in this custom buffer
    

    If you want the output generated by Write-Host, you need to check your instances Streams.Information property:

    $ps = [powershell]::Create().
        AddScript({ Write-Host 'hello world' })
    $ps.EndInvoke($ps.BeginInvoke())
    
    $ps.Streams.Information # => `hello world`
    

    Do note that Write-Host sends its output to the Information stream as opposed to Write-Output which sends it to Standard output (stdout), referred as Success stream by PowerShell.