Welcome to MSDN Blogs Sign in | Join | Help

Creating arbitrary delegates from scriptblocks in PowerShell...

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 opcode
function 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 call
emit Ldarg_0

emit Ldc_I4 ($argList.Count - 1)  # Create the parameter array
emit 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 method
emit 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)

Published Tuesday, July 25, 2006 10:31 PM by PowerShellTeam

Attachment(s): get-delegate.ps1

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# Interesting Finds: July 25, 2007

Tuesday, July 25, 2006 11:10 PM by Jason Haley

# re: Creating arbitrary delegates from scriptblocks in PowerShell...

The example worked great for me, but when I tried to make my own System.Net.Security.RemoteCertificateValidationCallback , it gets created okay but fails with "Invalid class token" whenever the delegate is invoked.
Wednesday, July 26, 2006 3:09 AM by Scott Yost

# re: Creating arbitrary delegates from scriptblocks in PowerShell...

I missed the type argument to the box instruction which was causing this problem. The script has been updated. I tried it with your example and it seems to work properly now.

-bruce

Bruce Payette [MSFT]
PowerShell Tech Lead
Wednesday, July 26, 2006 5:00 PM by PowerShellTeam

# re: Creating arbitrary delegates from scriptblocks in PowerShell...

Thank you - works great now!
Thursday, July 27, 2006 12:40 AM by Scott Yost

# re: Creating arbitrary delegates from scriptblocks in PowerShell...

Sorry if this is slightly off-topic, but I was wondering where we could submit requests for articles?

I'd very much like to see an article about creating dynamic types (I belive this involves the add-noteproperty function?).

Thanks, and keep up the good work
Monday, July 31, 2006 3:21 PM by Johnl

# re: Creating arbitrary delegates from scriptblocks in PowerShell...

> I was wondering where we could submit requests for articles?

We read all the comments so feel free to post a request here.

Jeffrey Snover [MSFT]
Windows PowerShell/Aspen Architect
Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx
Monday, July 31, 2006 5:03 PM by Jeffrey Snover

# A templating engine using PowerShell expressions

While working on our Software Factory for SharePoint 2007 solutions I needed to do some simple template

Saturday, December 30, 2006 8:40 PM by Serge van den Oever [Macaw]

# re: Creating arbitrary delegates from scriptblocks in PowerShell...

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

Saturday, December 30, 2006 8:57 PM by svdoever

# re: Creating arbitrary delegates from scriptblocks in PowerShell...

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

Monday, May 14, 2007 12:26 PM by Stefan Koell

# re: Creating arbitrary delegates from scriptblocks in PowerShell...

Is it possible to pass a powershell function as a delegate parameter to a .NET method?

Tuesday, May 29, 2007 12:22 AM by Omid

# re: Creating arbitrary delegates from scriptblocks in PowerShell...

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.

Tuesday, July 03, 2007 4:17 PM by Pangos

# re: Creating arbitrary delegates from scriptblocks in PowerShell...

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?

Tuesday, December 11, 2007 7:36 AM by Colin Bowern

# re: Creating arbitrary delegates from scriptblocks in PowerShell...

Can you use this to create a thread using PowerShell?

Tuesday, December 18, 2007 9:31 PM by Yuhong Bao

# re: Creating arbitrary delegates from scriptblocks in 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")

Sunday, January 06, 2008 8:17 AM by Richard

# re: Creating arbitrary delegates from scriptblocks in PowerShell...

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.

Wednesday, February 20, 2008 1:02 PM by Nikolaus Rollinger

# re: Creating arbitrary delegates from scriptblocks in PowerShell...

I'd like to see a solution to Colin's problem myself. I've been trying to solve it for a while now, but the IL is over my head.

Tuesday, April 15, 2008 3:01 AM by Brian Peiris

Leave a Comment

(required) 
required 
(required) 
 
Page view tracker