December 14th, 2010

Dynamic Method Invocation in PowerShell

  

Summary: Learn how to create a Windows PowerShell function to implement dynamic method invocation.

 

Hey, Scripting Guy! QuestionHey, Scripting Guy! What can you tell me about dynamic method invocation?

— PG

 

Hey, Scripting Guy! AnswerHello PG, Microsoft Scripting Guy Ed Wilson here. Today is day 2 of guest blogger week; we have Trevor Sullivan to tell us about dynamic method invocation.

 

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 we  call. 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-WmiObject into 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.

Get-WmiObject Win32_Process -Filter “Name = ‘notepad.exe'” | Invoke-Method -MethodName Terminate -MethodParameters $null

 

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

$MethodParams = @(“HKEY_LOCAL_MACHINE\Software”, “PoshTest”, “Trevor”)

Invoke-Method -InputObject ([Microsoft.Win32.Registry]) -MethodName SetValue -MethodParameters $MethodParams –Static

 

Invoke-Method function

Clear-Host

# $MethodName = ‘Terminate’

# $MethodParams = $null

# Get-WmiObject Win32_Process -Filter “Name = ‘notepad.exe'” | % { $_.$MethodName.Invoke() }

 

function Invoke-Method

{

      <#

            .Synopsis

            Invokes a method on objects.

           

            .Description

            Invokes a method on objects.

           

            .Parameter InputObject

            The objects you would like to invoke a method on.

           

            .Parameter MethodName

            The method you would like to invoke on the objects passed into the function.

           

            .Parameter MethodParameters

            An array of parameters to pass to the method during invocation.

           

            .Parameter Static

            Tell the function if you are calling a static method on the objects you pass to it. Default is $false.

           

            .Inputs

            An array of objects you would like to invoke a method on.

           

            .Outputs

            The results of the method invocation(s).

           

            .Link

            http://trevorsullivan.net

           

            .Link

            http://powershell.artofshell.com

           

      #>

     

      [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

      )

 

      Process

      {

            if ($MethodParameters -and $InputObject)

            {

                  if ($Static)

                  {

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

                  }

                  else

                  {

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

                  }

            }

            elseif ($InputObject)

            {

                  if ($Static)

                  {

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

                  }

                  else

                  {

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

                  }

            }

      }

}

 

# NOTE: If you want to use this as a module, uncomment this line.

# Export-ModuleMember -Function Invoke-Method -ErrorAction SilentlyContinue

 

Below are some more examples of using the Invoke-Method function.

################ WMI EXAMPLES WITHOUT PARAMETERS ################

 

# EXAMPLE: Start notepad.exe, then Terminate() it using Invoke-Method script cmdlet. Use -InputObject # parameter, not pipeline input.

notepad.exe

Invoke-Method -InputObject (Get-WmiObject Win32_Process -Filter “Name = ‘notepad.exe'”) -MethodName Terminate

# EXAMPLE: Start notepad.exe, then Terminate() it using Invoke-Method script cmdlet. Use pipeline # input, not -InputObject parameter.

notepad.exe

Get-WmiObject Win32_Process -Filter “Name = ‘notepad.exe'” | Invoke-Method -MethodName Terminate -MethodParameters $null

 

################ WMI EXAMPLES WITH PARAMETERS ################

 

# EXAMPLE: Create process using Invoke-Method and Win32_Process.Create()

Invoke-Method -InputObject ([wmiclass]”win32_process”) -MethodName Create -MethodParameters notepad.exe

 

# EXAMPLE: Add a mount point for an optical drive (change this if your optical drive letter isn’t E:

# NOTE: Win32_Volume REQUIRES that the target path exist prior to executing the method, otherwise you # will get a non-zero return value.

Invoke-Method -InputObject (Get-WmiObject -Class Win32_Volume -Filter “DriveLetter = ‘E:'”) -MethodName AddMountPoint -MethodParameters ‘c:\OpticalDrive’

 

# EXAMPLE: Delete the Win32_MountPoint instance created by previous call to AddMountPoint

Invoke-Method -InputObject (Get-WmiObject Win32_MountPoint | ? { $_.Directory -like ‘*optical*’ }) -MethodName Delete

 

################ EXAMPLES OF OTHER .NET OBJECTS ################

 

# EXAMPLE: Get the current process using the static method GetCurrentProcess() on # [System.Diagnostics.Process]

Invoke-Method -InputObject ([Diagnostics.Process]) -MethodName GetCurrentProcess -Static

 

# EXAMPLE: Create new process ‘notepad.exe’ using [System.Diagnostics.Process]

Invoke-Method -InputObject ([Diagnostics.Process]) -MethodName Start -MethodParameters notepad.exe -Static

 

# EXAMPLE: Kill all processes named ‘notepad’ via [System.Diagnostics.Process]

Invoke-Method -InputObject ([Diagnostics.Process]::GetProcessesByName(“notepad”)) -MethodName Kill

 

# EXAMPLE: Kill all processes named ‘notepad’ via [System.Diagnostics.Process]. Same as previous

# example, but using pipeline input.

[Diagnostics.Process]::GetProcessesByName(“notepad”) | Invoke-Method -MethodName Kill

 

# 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

$MethodParams = @(“HKEY_LOCAL_MACHINE\Software”, “PoshTest”, “Trevor”)

Invoke-Method -InputObject ([Microsoft.Win32.Registry]) -MethodName SetValue -MethodParameters $MethodParams -Static

 

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.

I invite you to follow me on Twitter or Facebook. If you have any questions, send email to me at scripter@microsoft.com or post them on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

 

Ed Wilson, Microsoft Scripting Guy 

Author

0 comments

Discussion are closed.