I’ve been here many of times when working with the console and creating a PowerShell instance and runspace to kick off a runspace in the background. I have everything situated and then call BeginInvoke() against my PowerShell object and realize my mistake when I see the PowerShellAsyncResult being displayed proudly in my console.
$Runspace = [runspacefactory]::CreateRunspace() $PowerShell = [powershell]::Create() $Runspace.Open() $PowerShell.Runspace = $Runspace [void]$PowerShell.AddScript({ #Imagine that this was some crazy long operation [pscustomobject]@{ Name = 'Boe Prox' PowerShell = $True } }) $PowerShell.BeginInvoke()
If you’ve worked with this before of done something where you had to use BeginInvoke() and not captured the resulting System.Management.Automation.PowerShellAsyncResult output, you know the pain in having to close out the console and re-running this and making sure to capture the output.
I had mentioned this in my talk on PowerShell runspaces that if you don’t capture this output, then it is the end of the line for your command running in the background.
It is painful. It is frustrating. And it is not the end of the world! There is actually hope for you if this happens and you do not want to kill whatever you are doing to restart the command in the runspace.
The trick lies in using Reflection to look into the PowerShell object for the invokeAsyncResult field and pulling the object out so you can use it later on with EndInvoke().
First we need to get the proper binding flags so we can look for our field. In this case, I only need the nonpublic and instance flags. I already know the name of the field: invokeAsyncResult from performing a all out look at the fields on my object.
Armed with this, I can now pull the field from the object.
$BindingFlags = [Reflection.BindingFlags]'nonpublic','instance' $Field = $PowerShell.GetType().GetField('invokeAsyncResult',$BindingFlags)
Now all I have to do is get the value of this field. In order to do that, I use the GetValue() method on the field and give it the PowerShell object as the required object in the parameter.
$Handle = $Field.GetValue($PowerShell)
We now have our PowerShellAsyncResult object back and can now use it to properly end the command using EndInvoke().
Perfect! Now we have a way to save ourselves if we happen to call BeginInvoke() on our PowerShell object and happen to forget to save the output object to use later on!
Filed under: powershell Tagged: asynchandle, Powershell, reflection, runspace
