Trevor Sullivan is a seasoned IT professional, experienced in large-scale systems management, with about 5 years of scripting experience. He picked up PowerShell in late 2007, and has been a fan ever since. At any point in time, Trevor can be found researching new technologies, writing code, or cooking up a new blog post at http://trevorsullivan.net. He is also very active in public social networks, such as Twitter (@pcgeek86) and various discussion forums and mailing lists. Here’s Trevor!

Background

There are situations where invoking a method on an object dynamically is desirable. Wait just a minute. What is dynamic method invocation, and why am I wasting my time reading this? Well, basically it means that you have an object, any ol’ object, and you want to invoke a method on it but you do not know what the method name is. In fact, you do not even know what the object is! It is a concept that is also known as late-binding.

Don Jones, a PowerShell MVP and trainer, recently posed some feedback on Microsoft Connect, suggesting that having an Invoke-Method cmdlet would be helpful in future versions of PowerShell. Microsoft has already included an Invoke-WmiMethod cmdlet but this does not work for arbitrary .NET objects.

In Don’s example on Connect, he uses the illustration that currently, in order to call a method on an array of objects, you must iterate over them using a foreach loop. He explains that this confuses new students to PowerShell, and could be resolved using a cmdlet that takes an array of objects as its input, and dynamically invokes a method on them.

Solution

I took this opportunity to develop a script cmdlet that more or less implements the previously mentioned functionality. A little-known feature of PowerShell is that you can invoke methods without hard-coding the method name. This also applies to an object’s properties. In fact, I realized after I had written this blog post that Jeffrey Snover wrote about this topic a few years back.

The script cmdlet I wrote lets you input a few things:

An object, or array of objects

A method name to call

Method parameters

Specify whether the method is static (optional, default is $false)

Stepping Through the Code

Declaring the Invoke-Method Function

First we start off by defining a function named Invoke-Method, and declaring the CmdletBinding attribute which turns the script cmdlet into an advanced function. Then, we declare the parameters that we will pass into the function. We have to pass an object (or an array of objects) into the function which has a method wecall. Therefore, we declare a parameter named $InputObject. We also have to specify the method name that we want to invoke on the objects we pass to the function. For this, we declare a parameter named $MethodName. Finally, if the method requires that we pass parameters into it, we need some way to pass parameters into Invoke-Method. For this purpose, we declare a $MethodParameters function parameter which is an array of all the items to be passed into the method call during invocation.

Note: For the input objects, we use ValueFromPipeline to allow the ability to pipe objects into the function. With this option defined, you have the option of either using the –InputObject parameter, or pipeline input. It is your choice.

[CmdletBinding()]

param

(

[Parameter(ValueFromPipeline = $true, Position = 0)]

[Object[]]

$InputObject,

[Parameter(Mandatory = $true, HelpMessage = "Please enter the name of the method you would like to invoke.")]

[string]

$MethodName,

[String[]]

$MethodParameters = $null,

[switch]

$Static = $false

Next, we declare the Process { … } block of the Invoke-Method function. This is where methods will be called on the object passed into the function. We first start with a check to see whether any parameters were passed in, and make sure that the objects that were passed in are not null. If method parameters have been specified in a call to Invoke-Method, then we have to make sure that we call the method with the parameters. We also have to check whether the –Static switch was specified, because if it was, then we must call the method using the (::) operator, instead of the (.) operator. As you will see below, the “cool” part is that we are calling a method on the object by using the $MethodName parameter, instead of hard-coding the method name into our script.

if ($MethodParameters -and $InputObject)

{

if ($Static)

{

$InputObject | % { $_::$MethodName.Invoke($MethodParameters) }

}

else

{

$InputObject | % { $_.$MethodName.Invoke($MethodParameters) }

}

}

Similar to the above code, we must do the same thing to call a method without any parameters. In our else { … } statement, we use similar code to check for the existence of input objects, and check for the –Static switch. The dynamic method calls are the same, by using the $MethodName parameter to specify the name of the method we want to call.

elseif ($InputObject)

{

if ($Static)

{

$InputObject | % { $_::$MethodName.Invoke() }

}

else

{

$InputObject | % { $_.$MethodName.Invoke() }

}

}

That sums up the function definition. Now we can look at some examples of how to use it!

Usage Examples

Here is an example that invokes the WMI Terminate() method on all instances of notepad.exe. This example uses PowerShell pipeline input to pipe the output from Get-WmiObjectinto the Invoke-Method function. Of course, PowerShell already has an Invoke-WmiMethod function so this is functionality that is provided out of the box. The next example however, is different.

In the following figure, we call a static method on the .NET [Microsoft.Win32.Registry] type. This object has a SetValue() static method which sets a registry value in a given registry hive, subkey, name, and value. The first line constructs an array of parameters that we will pass to the method, and the second line invokes it while passing in the $MethodParams array. Because we are calling a static method, we specify the –Static switch parameter as defined on the Invoke-Method function definition.

# EXAMPLE: Set a registry value using the SetValue() static method on [Microsoft.Win32.Registry]

# NOTE: SetValue() has two overloads, so this is a good test to determine which overload the CLR selects

PG, that is all there is to using dynamic method execution. Thank you, Trevor. Guest blogger week will continue tomorrow when Rhys CampbelI will talk about mapping SQL Server objects via Windows PowerShell.