Automating the world one-liner at a time…
People have been asking about creating arbitrary delegates out of scriptblocks. As Jeffrey has mentioned, this isn't directly supported in V1.0 of PowerShell. It is, however, possible to do it using dynamic methods and the CreateDelegate call. Here's a script (also attached) that will do it. I haven't tested this very much so it may not work in all cases but it should give you an idea of what's possible. Note - this is a fairly extreme (pathological?) example of what can be done in PowerShell. You can use the script as shown in the following example:
$delegate = ./get-delegate `
System.Text.RegularExpressions.MatchEvaluator {
# Return a replacement for the matching string... "<$($args[0].ToString().ToLower())>"
# and count the number of replacements... $global:PatternCount++}
$re = [regex] "[A-Z]"
# now transform some text...
get-help about_Assignment_operators | %{ $re.Replace($_, $delegate) }
# And display the number of replacements that were done...
"`nNumber of replacements: $PatternCount"
This example uses the MatchEvaluator delegate to do some transformations on a piece of text. The script text follows... (updated - I missed the type argument to the box instruction in the first version posted.)
Bruce Payette
PowerShell Tech Lead
(And for more hardcore hacking - check out Lee's blog entry:
http://www.leeholmes.com/blog/MorePInvokeInPowerShell.aspx )
--------------------cut here-----------------------------------------------param([type] $type, [ScriptBlock] $scriptBlock)
# Helper function to emit an IL opcodefunction emit($opcode){ if ( ! ($op = [System.Reflection.Emit.OpCodes]::($opcode))) { throw "new-method: opcode '$opcode' is undefined" }
if ($args.Length -gt 0) { $ilg.Emit($op, $args[0]) } else { $ilg.Emit($op) }}
# Get the method info for this delegate invoke...$delegateInvoke = $type.GetMethod("Invoke")
# Get the argument type signature for the delegate invoke$parameters = @($delegateInvoke.GetParameters())$returnType = $delegateInvoke.ReturnParameter.ParameterType
$argList = new-object Collections.ArrayList[void] $argList.Add([ScriptBlock])foreach ($p in $parameters){ [void] $argList.Add($p.ParameterType);}
$dynMethod = new-object reflection.emit.dynamicmethod ("", $returnType, $argList.ToArray(), [object], $false)$ilg = $dynMethod.GetILGenerator()
# Place the scriptblock on the stack for the method callemit Ldarg_0
emit Ldc_I4 ($argList.Count - 1) # Create the parameter arrayemit Newarr ([object])
for ($opCount = 1; $opCount -lt $argList.Count; $opCount++){ emit Dup # Dup the array reference emit Ldc_I4 ($opCount - 1); # Load the index emit Ldarg $opCount # Load the argument if ($argList[$opCount].IsValueType) # Box if necessary { emit Box $argList[$opCount] } emit Stelem ([object]) # Store it in the array}
# Now emit the call to the ScriptBlock invoke methodemit Call ([ScriptBlock].GetMethod("InvokeReturnAsIs"))
if ($returnType -eq [void]){ # If the return type is void, pop the returned object emit Pop}else{ # Otherwise emit code to convert the result type which looks # like LanguagePrimitives.ConvertTo(value, type)
$signature = [object], [type] $convertMethod = [Management.Automation.LanguagePrimitives].GetMethod( "ConvertTo", $signature); $GetTypeFromHandle = [Type].GetMethod("GetTypeFromHandle"); emit Ldtoken $returnType # And the return type token... emit Call $GetTypeFromHandle emit Call $convertMethod}emit Ret
## Now return a delegate from this dynamic method...#
$dynMethod.CreateDelegate($type, $scriptBlock)
While working on our Software Factory for SharePoint 2007 solutions I needed to do some simple template
Thanks for your great code, you saved my day! I used it to create a simple template engine with PowerShell expression expansion. Have a look at http://blogs.msdn.com/powershell/archive/2006/07/25/678259.aspx
Hi,
can someone explain how this works exactly. I need to create a SSLStream and must provide a callback method in the constructor and I really cannot find out how to use your example to do that. Can you provide some detailed instructions?
cheers
Is it possible to pass a powershell function as a delegate parameter to a .NET method?
You updated the script on the page, but not in the attachment. If you use this utility, be sure to use version on the Blog page.
I'm trying to create a Predicate using this method but the result isn't correct:
$domains = New-Object "System.Collections.Generic.List``1[System.String]";
$domains.Add("foo.com");
$uri = New-Object System.Uri("http://bar.com");
$delegate = get-delegate "System.Predicate``1[System.String]" { Write-Host $uri.Host " : " $args " = " ([String]::Compare($uri.Host, $args[0], $true) -eq 0); return ([String]::Compare($uri.Host, $args[0], $true) -eq 0); };
$domains.Exists($delegate);
The call to Exists returns true even though bar.com isn't in the collection.
Thoughts on how where I'm going wrong?
Can you use this to create a thread using PowerShell?
Cool script, but crashs PowerShell with the following:
PS E:\> $d = &get-delegate System.Threading.WaitCallback `
>> {
>> echo $args[0]
>> }
>>
PS E:\> [System.Threading.ThreadPool]::QueueUserWorkItem ($d, "test")
I've detected the same problem as already posted by Colin Bowern.
Is this not important enough to give it your attention?
Hey, Jeff and Bruce, give it the necessary attention, please. Don't forget we are your customers!
I hope this helps!
Thank you.