{"id":9911,"date":"2006-07-25T17:31:00","date_gmt":"2006-07-25T17:31:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/powershell\/2006\/07\/25\/creating-arbitrary-delegates-from-scriptblocks-in-powershell\/"},"modified":"2019-02-18T13:21:29","modified_gmt":"2019-02-18T20:21:29","slug":"creating-arbitrary-delegates-from-scriptblocks-in-powershell","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/powershell\/creating-arbitrary-delegates-from-scriptblocks-in-powershell\/","title":{"rendered":"Creating arbitrary delegates from scriptblocks in PowerShell&#8230;"},"content":{"rendered":"<p class=\"MsoNormal\">People have been asking about creating arbitrary delegates out of scriptblocks. As Jeffrey has mentioned, this isn&#8217;t directly supported in V1.0 of PowerShell. It is, however, possible to do it using dynamic methods and the CreateDelegate call. Here&#8217;s a script (also attached) that will do it. I haven&#8217;t tested this very much so it may not work in all cases but it should give you an idea of what&#8217;s possible. Note &#8211; 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:<\/p>\n<p class=\"MsoNormal\">&nbsp;<\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\">$delegate = .\/get-delegate `<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\">System.Text.RegularExpressions.MatchEvaluator {<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\">&nbsp;&nbsp;&nbsp; # Return a replacement for the matching string&#8230;<br \/>&nbsp;&nbsp;&nbsp; &#8220;&lt;$($args[0].ToString().ToLower())&gt;&#8221;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\">&nbsp;&nbsp;&nbsp; # and count the number of replacements&#8230;<br \/>&nbsp;&nbsp;&nbsp; $global:PatternCount++<br \/>}<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\"><\/font>&nbsp;<\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\">$re = [regex] &#8220;[A-Z]&#8221;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\"><\/font>&nbsp;<\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\"># now transform some text&#8230;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\"><\/font>&nbsp;<\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\">get-help about_Assignment_operators |<br \/>&nbsp;&nbsp; %{ $re.Replace($_, $delegate) }<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\"><\/font>&nbsp;<\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\"># And display the number of replacements that were done&#8230;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\">&#8220;`nNumber of replacements: $PatternCount&#8221;<\/font><\/p>\n<p class=\"MsoNormal\">&nbsp;<\/p>\n<p class=\"MsoNormal\">This example uses the MatchEvaluator delegate to do some transformations on a piece of text. The script text follows&#8230; (updated &#8211; I missed the type argument to the box instruction in the first version posted.)<\/p>\n<p class=\"MsoNormal\">&nbsp;<\/p>\n<p class=\"MsoNormal\">Bruce Payette<\/p>\n<p class=\"MsoNormal\">PowerShell Tech Lead<\/p>\n<p class=\"MsoNormal\">&nbsp;<\/p>\n<p class=\"MsoNormal\">(And for more hardcore hacking &#8211; check out Lee&#8217;s blog entry:<\/p>\n<p class=\"MsoNormal\"><a href=\"http:\/\/www.leeholmes.com\/blog\/MorePInvokeInPowerShell.aspx\">http:\/\/www.leeholmes.com\/blog\/MorePInvokeInPowerShell.aspx<\/a>&nbsp;)<\/p>\n<p class=\"MsoNormal\">&nbsp;<\/p>\n<p class=\"MsoNormal\"><font face=\"Arial\">&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;cut here&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br \/><\/font><font face=\"Courier New\" size=\"2\">param([type] $type, [ScriptBlock] $scriptBlock)<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\"># Helper function to emit an IL opcode<br \/>function emit($opcode)<br \/>{<br \/>&nbsp;&nbsp;&nbsp; if ( ! ($op = [System.Reflection.Emit.OpCodes]::($opcode)))<br \/>&nbsp;&nbsp;&nbsp; {<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw &#8220;new-method: opcode &#8216;$opcode&#8217; is undefined&#8221;<br \/>&nbsp;&nbsp;&nbsp; }<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\">&nbsp;&nbsp;&nbsp; if ($args.Length -gt 0)<br \/>&nbsp;&nbsp;&nbsp; {<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $ilg.Emit($op, $args[0])<br \/>&nbsp;&nbsp;&nbsp; }<br \/>&nbsp;&nbsp;&nbsp; else<br \/>&nbsp;&nbsp;&nbsp; {<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $ilg.Emit($op)<br \/>&nbsp;&nbsp;&nbsp; }<br \/>}<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\"># Get the method info for this delegate invoke&#8230;<br \/>$delegateInvoke = $type.GetMethod(&#8220;Invoke&#8221;)<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\"># Get the argument type signature for the delegate invoke<br \/>$parameters = @($delegateInvoke.GetParameters())<br \/>$returnType = $delegateInvoke.ReturnParameter.ParameterType<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\">$argList = new-object Collections.ArrayList<br \/>[void] $argList.Add([ScriptBlock])<br \/>foreach ($p in $parameters)<br \/>{<br \/>&nbsp;&nbsp;&nbsp; [void] $argList.Add($p.ParameterType);<br \/>}<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\">$dynMethod = new-object reflection.emit.dynamicmethod (&#8220;&#8221;,<br \/>&nbsp;&nbsp;&nbsp; $returnType, $argList.ToArray(), [object], $false)<br \/>$ilg = $dynMethod.GetILGenerator()<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\"># Place the scriptblock on the stack for the method call<br \/>emit Ldarg_0<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\">emit Ldc_I4 ($argList.Count &#8211; 1)&nbsp; # Create the parameter array<br \/>emit Newarr ([object])<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\">for ($opCount = 1; $opCount -lt $argList.Count; $opCount++)<br \/>{<br \/>&nbsp;&nbsp;&nbsp; emit Dup&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Dup the array reference<br \/>&nbsp;&nbsp;&nbsp; emit Ldc_I4 ($opCount &#8211; 1); # Load the index<br \/>&nbsp;&nbsp;&nbsp; emit Ldarg $opCount&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Load the argument<br \/>&nbsp;&nbsp;&nbsp; if ($argList[$opCount].IsValueType) # Box if necessary<br \/>&nbsp;{<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; emit Box $argList[$opCount]<br \/>&nbsp;}<br \/>&nbsp;&nbsp;&nbsp; emit Stelem ([object])&nbsp; # Store it in the array<br \/>}<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\"># Now emit the call to the ScriptBlock invoke method<br \/>emit Call ([ScriptBlock].GetMethod(&#8220;InvokeReturnAsIs&#8221;))<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\">if ($returnType -eq [void])<br \/>{<br \/>&nbsp;&nbsp;&nbsp; # If the return type is void, pop the returned object<br \/>&nbsp;&nbsp;&nbsp; emit Pop<br \/>}<br \/>else<br \/>{<br \/>&nbsp;&nbsp;&nbsp; # Otherwise emit code to convert the result type which looks<br \/>&nbsp;&nbsp;&nbsp; # like LanguagePrimitives.ConvertTo(value, type)<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\">&nbsp;&nbsp;&nbsp; $signature = [object], [type]<br \/>&nbsp;&nbsp;&nbsp; $convertMethod =<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Management.Automation.LanguagePrimitives].GetMethod(<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8220;ConvertTo&#8221;, $signature);<br \/>&nbsp;&nbsp;&nbsp; $GetTypeFromHandle = [Type].GetMethod(&#8220;GetTypeFromHandle&#8221;);<br \/>&nbsp;&nbsp;&nbsp; emit Ldtoken $returnType&nbsp; # And the return type token&#8230;<br \/>&nbsp;&nbsp;&nbsp; emit Call $GetTypeFromHandle<br \/>&nbsp;&nbsp;&nbsp; emit Call $convertMethod<br \/>}<br \/>emit Ret<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\">#<br \/># Now return a delegate from this dynamic method&#8230;<br \/>#<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Courier New\" size=\"2\">$dynMethod.CreateDelegate($type, $scriptBlock)<br \/><\/font><\/p>\n<p><a href=\"https:\/\/msdnshared.blob.core.windows.net\/media\/MSDNBlogsFS\/prod.evol.blogs.msdn.com\/CommunityServer.Components.PostAttachments\/00\/00\/67\/82\/59\/get-delegate.ps1\">get-delegate.ps1<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>People have been asking about creating arbitrary delegates out of scriptblocks. As Jeffrey has mentioned, this isn&#8217;t directly supported in V1.0 of PowerShell. It is, however, possible to do it using dynamic methods and the CreateDelegate call. Here&#8217;s a script (also attached) that will do it. I haven&#8217;t tested this very much so it may [&hellip;]<\/p>\n","protected":false},"author":600,"featured_media":13641,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[53,54],"class_list":["post-9911","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-powershell","tag-delegates","tag-scriptblock"],"acf":[],"blog_post_summary":"<p>People have been asking about creating arbitrary delegates out of scriptblocks. As Jeffrey has mentioned, this isn&#8217;t directly supported in V1.0 of PowerShell. It is, however, possible to do it using dynamic methods and the CreateDelegate call. Here&#8217;s a script (also attached) that will do it. I haven&#8217;t tested this very much so it may [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts\/9911","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/users\/600"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/comments?post=9911"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts\/9911\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/media\/13641"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/media?parent=9911"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/categories?post=9911"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/tags?post=9911"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}