Invoking Private Static Methods Using PowerShell

Usually if I want to accomplish a task in PowerShell, I can count on either a cmdlet or a publicly available .Net method to come to my rescue. Sometimes though, a method that I would like to use is just seemingly not available. Fortunately on occasion I can find a method that does what I need (or at least sets me up to accomplish my goal a little easier) using a private (or hidden) static method.

An example of needing such a method was when I decided to incorporate the $Using: variable scope in my PoshRSJob module. I had to open up and dive into PowerShell using DotPeek to see the internal workings of Start-Job to see that there were some calls to a method which I could not see just by piping the object into Get-Member and viewing the methods. Instead, we have to take the object and dive into the guts of it to view some methods and also enlist the help of some reflection binding flags to actually uncover the method that we need.

I won’t actually go into this approach as it won’t really provide a good ‘real world’ use. Instead I will take a look at the [Array] type and we will uncover some private methods and use one to see what happens.

Lets take a look at the static methods on [Array] and see what we have:

[Array] | Get-Member –Type Method –Static

image

You might be wondering why I used the –Static switch instead of looking at just the Methods. This is because I only wanted to display the methods that would actually be associated with working with the Array in this case. Had I not done that, then we would be seeing methods associated with the System.RuntimeType object.

What isn’t shown here is a method within Array called GetMedian, which as you can tell, should help to tell you the middle item in an array. Also note that it isn’t even mentioned on the MSDN site for Array. Why would you need this? Well, there is always someone somewhere that might need this type of thing.

With that, we need to figure out how we can find this method. Looking at the methods for the System.RunTimeType object of Array, we can find two methods that stick out: GetMethod() and GetMethods(). Calling GetMethods() will spit out all of the methods of Array while GetMethod() is geared towards a particular method.

image

Taking a deeper look at the method overloads, we can see that there are more than a few overloads that we could use for each of these methods.

image

In this example, I just want to see all methods (just the names) for Array.

[Array].GetMethods().Name | Group | Select Count, Name

image

You can see the same static methods that we observed earlier, plus multiple methods with the same name. Let’s say that we wanted to look at a single method. We would just list the name of the method in GetMethod and then we can view more information about it.

[Array].GetMethod(‘Clear’)

image

Calling GetParameters() from this method will show us all of the parameters required for this method, if applicable. In the case of Clear, there are no parameters so we do not have to worry about adding a parameter to the private method if we were to call it. Don’t worry, we will see some parameters for GetMedian Winking smile.

Before we hit up on GetMedian, let’s examine what happens when we try to view a method that has multiple overloads.

[array].GetMethod('Reverse')

image

This error is talking about results for ‘Reverse’ were found which means that we need to find a way to only use a single instance. What we need to do is figure out the parameter types that exist for each method overload and we can then use those with our GetMethod() call.

[array].GetMethods() | Where {$_.Name -eq 'reverse'} | ForEach {
    Write-Verbose $($_.Name) -Verbose
    $_.GetParameters() | Select Name, ParameterType, Position
}

image

I’ll got for the second method which is looking for an Array and two Int32 types and plug those into the method and see what happens.

[array].getmethod('Reverse',[type[]]@('array','int32','int32')).GetParameters()

image

And now we have our method. Definitely something to keep in mind if you happen to run into this error message.

The problem here is that the GetMedian method is not here! This is now where we have to make use of the Reflection.BindingFlags to locate our private method.

Let’s take a look at all of the possible values here for System.Reflection.BindingFlags

[System.Reflection.BindingFlags].GetEnumNames()

image

Each of these provides specific filters for when you do a search for the methods using GetMethod() or GetMethods(). Rather than regurgitate all of the possible meanings for these, I will instead direct you to this link that does a fine job of explaining everything.

The binding flags that I need to locate the private methods are: Static, Nonpublic and Instance. The three of these will open up a whole new world of methods that were previously hidden from us.

$BindingFlags = 'static','nonpublic','instance'
[array].GetMethods($BindingFlags).Name

SNAGHTML2be7092d

Definitely a lot of private methods here! But in this case, I am only concerned with GetMedian. Let’s now view the method itself.

$BindingFlags = 'static','nonpublic','instance'
[array].GetMethod(‘GetMedian’,$BindingFlags)

image

Now we can view the parameters of this method and see what is required.

$Method = [array].getmethod('GetMedian',@('nonpublic','instance','static'))
$Method.GetParameters() | 
Select Name, ParameterType, Position | Format-Table –AutoSize

image

Here we can see that both of the parameters require an Int32. The low is the starting index of the array (usually 0) and the hi parameter requires the highest index of the array. Also note the positions of each parameter. Just like in PowerShell, if we don’t specify the correct value for the right parameter in the proper position, bad things can happen.

We have the method and we have the parameters, but how in the world do we actually use this method? It’s not publically available so what is the magical process to use it? Well, we have to call the Invoke() method on the method and supply the object that owns the method followed by the collection of values required by the parameters (in the proper order) for it to work properly.

I want to first create an array of letters:

$list = 'a','b','c','d','e'

image

We obviously know that ‘c’ is the middle item in the array, but imagine if this was a huge array. Might not be that obvious.

Next up is to set ourselves up to use the private method.

$GetMedian = [array].getmethod('GetMedian',@('nonpublic','instance','static'))

And now we get to call the method by supplying the array itself and the low and hi parameters.

$Index = $GetMedian.Invoke($list,@(0,$list.Count))
$Index

image

What we actually get back is the index of the array (remember the return type of the method) so we can use that to slice into the array and verify that the median is indeed ‘c’.

$list[$Index]

image

Sure enough, it is the letter ‘c’ that is the median of this array.

Given, this was a rather simple demonstration of how you can use private methods in PowerShell, but this was the same approach that I took when working with my module to implement $Using: support.

Just for the sake of having a reference, if I ran into a issue with multiple method overloads, I would use the following in order to get the one that I needed (after knowing the parameter types).

[array].getmethod('GetMedian',@('nonpublic','instance','static'),$Null,[type[]]@([int32],[int32]),$null)

It just took a little work to locate the method and then to supply the proper parameter types to the method and it worked like a champ!

About Boe Prox

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

6 Responses to Invoking Private Static Methods Using PowerShell

  1. github.com/oising/poke

    PS> install-module poke

  2. Dmitry Gancho says:

    Great article, thank you! What I’ve found though is that supplying the array itself when calling ‘invoke’ is not needed to get the same result ($list.Count equals 5 in the example):

    $GetMedian = [array].getmethod(‘GetMedian’,(‘nonpublic’,’instance’,’static’))
    $GetMedian.Invoke($null,(0,5))
    2

  3. Glenn Faustino says:

    Very informative, thanks for sharing Boe!

  4. MikeH says:

    Hi great write up!

    I think, with the current trend for evil Powershell, there are many possibilities to harvest interesting things with this reflection technique.

    Thanks.

    Mike

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