# 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