Yet Another Way to Get Output from a Runspace, Reflection Edition

While working on some possible feature updates to my module, PoshRSJob, I have been looking at ways to get the output from a runspace without relying on the usual approach of using EndInvoke() while supplying a PowerShellAsyncResult object to pull the output. I had blogged about one way to get output from a runspace, but have found another way to grab the data as well, this time using reflection.

Now why am I continuing on this path to find more ways to get data from a runspace when there are already perfectly good ways to accomplish this? Well, one of my bigger struggles with making PoshRSJob more like the typical PSJobs that we all know and love are that it is able to preserve the streams properly whereas in PoshRSJob, we have to do some dancing to make it all work…almost. Write-Host continues to be a pain point as it is hard to find a good way to ensure that the stream is outputted not only properly, but with the proper colors defined when using the cmdlet. Now, I haven’t solved this yet and don’t plan on solving this any time soon, but by doing some experimenting, I have found some other ways that I wanted to share with you in viewing output from a runspace without ending the invocation of the runspace. My thoughts are that if I can find a different way to peek into a runspace to find if information is available,then I could refactor my code to provide a one time receive of the job to show all of the streams while still keeping the output objects available to until disposed of. Will this be solved? Maybe, but it is fun to keep digging into runspaces and this time, I get to do so using reflection! One of my favorite techniques to work with.

There really is not a lot to doing this, but we will cover from setup to tear down so you can easily copy and paste this for your own testing. First off, we build of and kick off the runspace like we usually do.

#Begin using the proces that we know and love
$PowerShell = [powershell]::Create()
$Runspace = [runspacefactory]::CreateRunspace()
$Runspace.Open()
$PowerShell.Runspace = $Runspace
[void]$PowerShell.AddScript({
    [pscustomobject]@{
        Data = 1
        Text = 'Test'
    }
})

$Handle = $PowerShell.BeginInvoke()

I am holding onto the PowerShellAsyncResult just out of habit, but in the end, we can just dispose of the runspace without calling EndInvoke(), or we can still call it and verify that the output lines up with what we will see using reflection. By the way, this is a great way to still get your output if you happen to forget the object in your travels.

#Specfiy the required flags to pull the output stream
$Flags = 'nonpublic','instance','static'
$Output = $PowerShell.GetType().GetProperty('OutputBuffer',$Flags)

image

Now that we have the OutputBuffer property object, we can then make use of the GetValue() method to pull the output data from the runspace. We will this by supplying the $PowerShell variable so it knows where and what to do to get the data it needs.

$Output.GetValue($PowerShell)

image

What this gives us is another way to maybe see if there is data available by looking at the count of the results and if there are more than 0, then we could proceed with processing of the runspace or just dispose of it without any worry. And in case you are wondering about the whole count approach, keep in mind that the actual object is of the System.Management.Automation.PSDataCollection`1[[System.Management.Automation.PSObject, System.Management.Automation, Version=3.0.0.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35]] type.

Get-Member -InputObject ($Output.GetValue($PowerShell))

image

Don’t call this method prematurely if the runspace is still processing data. This actually opens up the runspace to your current console session and will block any further input until the runspace has completed.

That is all for peeking into a completed runspace using reflection and PowerShell. While this will not likely be something that you would use often, it does provide an alternative means to looking at the data in a runspace without using EndInvoke().

This entry was posted in powershell and tagged , , . Bookmark the permalink.

Leave a comment