# encoding: ascii # api: powershell # title: invoke-anything # description: Late bound invocation for any methods/generic methods (or properties), including open generic members that would normally require awkward reflection and near-impossible manual generic type parameter inference. Pipe the type or instance, pass the method name as a string, the arguments as an object array and don’t forget to tell it if you want to call a static method. # version: 0.1 # type: function # author: Oisin Grehan # license: CC0 # function: Invoke-LateBoundMember # x-poshcode-id: 5515 # x-archived: 2015-07-24T21:13:04 # x-published: 2015-10-14T18:58:00 # # function Invoke-LateBoundMember { [cmdletbinding(supportsshouldprocess)] param( [parameter(valuefrompipeline, mandatory)] [validatenotnull()] [psobject[]]$InputObject, [parameter(mandatory, position=0)] [validatenotnullorempty()] [string]$MemberName, [parameter(position=1, valuefromremainingarguments)] [object[]]$ArgumentList, [parameter()] [switch]$Static, [parameter()] [switch]$IgnoreReturn ) begin { add-type -AssemblyName Microsoft.VisualBasic $vbbinder = [Microsoft.VisualBasic.CompilerServices.NewLateBinding] } process { foreach ($element in $InputObject) { $instance = $null $type = $null if ($Static) { $type = $element } else { $instance = $element } # this lets us know if $copyBack = new-object bool[] $ArgumentList.Length # unwrap [ref] args # can't use pipeline here as the new array elements get wrapped in psobject # this is a problem because the .net method binder won't unwrap if the destination # is typed as object (because psobject is compatible with this signature, right?) #$unwrapped = $argumentlist | % { if ($_ -is [ref]) { $_.value } else { $_ } } $unwrapped = new-object object[] $ArgumentList.Count if ($ArgumentList) { [array]::Copy($ArgumentList, $unwrapped, $ArgumentList.length) for ($i = 0; $i -lt $ArgumentList.Length; $i++) { if ($unwrapped[$i] -is [ref]) { $unwrapped[$i] = $unwrapped[$i].value } if ($unwrapped[$i] -is [psobject]) { write-warning "The argument at index $i is wrapped as a PSObject. This is almost certainly not what you want." } } } $result = $vbbinder::LateCall( [object]$instance, # null if static call [type]$type, # null if instance call [string]$MemberName, $unwrapped, <# ArgumentNames #> $null, # not used <# TypeArguments #> $null, # let dlr infer for us $copyBack, # was arg modified byref? $IgnoreReturn.IsPresent) # should ignore/expect retval? # check for copyback / byref for ($i = 0; $i -lt $ArgumentList.Length; $i++) { $argument = $ArgumentList[$i] if ($copyBack[$i]) { # update psreference if ($argument -is [ref]) { write-verbose "Updating [ref] at index $i" $argument.value = $unwrapped[$i] } else { write-warning "Argument at index $i was modified. Pass the argument as a [ref] variable to receive the result." } } } if (-not $IgnoreReturn) { $result } } } } New-Alias -Name ilb -Value Invoke-LateBoundMember -Force