Finding the PowerShellAsyncResult on PowerShell Object After Using BeginInvoke

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()

SNAGHTML4ec3557

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)

image

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)

SNAGHTML4f785f2

We now have our PowerShellAsyncResult  object back and can now use it to properly end the command using EndInvoke().

image

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!

About Boe Prox

Microsoft Cloud and Datacenter MVP working as a SQL DBA.
This entry was posted in powershell and tagged , , , . Bookmark the permalink.

2 Responses to Finding the PowerShellAsyncResult on PowerShell Object After Using BeginInvoke

  1. Kevin Knox says:

    Thank you, Boe. I’m wearing out these posts and enjoying it.
    I’m using the runspace concept and BeginInvoke to parallelize calls to 700 unix servers using plink, and currently piloting with 50. Of the 50 a dozen or so hang on bad passwords. So, I have a dozen runspaces hung with active plink calls. If I close the PS window, all those plinks die. I’d rather kill the hung runspaces after a given timeout and report the closures politely. Runspace.close() hangs. Thread.stop() closes the open window, but leaves all the hung runspaces active and hung.
    Is there a rude but clean way to kill an unfinished thread in a runspace?

  2. Jaap Brasser says:

    Interesting, I usually would just re-run the command and get some coffee while it runs. Thanks for the detailed explanation of how to recover from this.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s