lib/pcall.ps1
using namespace System.Linq using namespace System.Reflection using namespace System.Reflection.Emit using namespace System.Linq.Expressions using namespace System.Runtime.InteropServices function New-Delegate { [CmdletBinding()] param( [Parameter(Mandatory, Position=0)] [ValidateNotNullOrEmpty()] [String]$Module, [Parameter(Mandatory, Position=1)] [ValidateScript({![String]::IsNullOrEmpty($_)})] [ScriptBlock]$Signature, [Parameter()] [Switch]$LVal ) begin { if (!($stash = $ExecutionContext.SessionState.PSVariable.Get('PwsHandlesStash'))) { $stash = Set-Variable -Name PwsHandlesStash -Value ( [IntPtr[]]@() ) -Visibility Private -Scope Global -PassThru } $kernel32 = @{} [Array]::Find(( # GetModuleHandle, GetProcAddress and LoadLibrary Add-Type -AssemblyName Microsoft.Win32.SystemEvents -PassThru ), [Predicate[Type]]{$args[0].Name -eq 'kernel32'}).GetMethods( [BindingFlags]'NonPublic, Static, Public' ).Where{$_.Name -cmatch '\A(Get|Load)(P|M|L)'}.ForEach{$kernel32[$_.Name] = $_} if (($mod = $kernel32.GetModuleHandle.Invoke($null, @($Module))) -eq [IntPtr]::Zero) { if (($mod = $kernel32.LoadLibrary.Invoke($null,@($Module))) -eq [IntPtr]::Zero) { throw [DllNotfoundException]::new("Cannot find $Module library.") } $stash.Value += $mod } } process {} end { $funcs = @{} for ($i, $m, $fn, $p = 0, ([Expression].Assembly.GetType( 'System.Linq.Expressions.Compiler.DelegateHelpers' ).GetMethod('MakeNewCustomDelegate', [BindingFlags]'NonPublic, Static') ), [Marshal].GetMethod('GetDelegateForFunctionPointer', ([IntPtr])), $Signature.Ast.FindAll({$args[0].CommandElements}, $true).ToArray(); $i -lt $p.Length; $i++ ) { $fnret, $fname = ($def = $p[$i].CommandElements).Value if (($fnsig = $kernel32.GetProcAddress.Invoke($null, @($mod, $fname))) -eq [IntPtr]::Zero) { throw [InvalidOperationException]::new("Cannot find $fname signature.") } $fnargs = $def.Pipeline.Extent.Text [Object[]]$fnargs = [String]::IsNullOrEmpty($fnargs) ? $fnret : ( ($fnargs -replace '\[|\]' -split ',\s+?').ForEach{ $_.StartsWith('$') ? (Get-Variable $_.Remove(0, 1) -ValueOnly) : $_ } + $fnret ) $funcs[$fname] = $fn.MakeGenericMethod( [Delegate]::CreateDelegate([Func[[Type[]], Type]], $m).Invoke($fnargs) ).Invoke([Marshal], $fnsig) } if ($LVal) { return $funcs } # do not establish variable automatically Add-Member -InputObject $funcs -Name Dispose -MemberType ScriptMethod -Value { if (!($stash = $ExecutionContext.SessionState.PSVariable.Get('PwsHandlesStash')).Value) { return # nothing to release } $kernel32 = New-Delegate -Module kernel32 -Signature { Boolean FreeLibrary([IntPtr]) } -LVal [ParallelEnumerable]::Reverse([ParallelEnumerable]::AsParallel($stash.Value)).ForEach{ if (!([Boolean]$res = $kernel32.FreeLibrary.Invoke($_))) { Write-Warning $res } } $stash.Value = [IntPtr[]]@() } Set-Variable -Name $Module -Value $funcs -Scope Script -Force } } function New-ILMethod { [CmdletBinding()] param( [Parameter(Mandatory, Position=0)] [ValidateNotNullOrEmpty()] [String]$Noun, [Parameter(Mandatory, Position=1)] [ValidateNotNullOrEmpty()] [String]$Code, [Parameter()][Type]$ReturnType = [void], [Parameter()][Type[]]$Parameters = @(), [Parameter()][ScriptBlock]$Variables ) end { $il = ($dm = [DynamicMethod]::new($Noun, $ReturnType, $Parameters)).GetILGenerator() if ($Variables) { $Variables.Ast.FindAll({$args[0].CommandElements}, $true).ToArray().ForEach{ Set-Variable -Name $($_.CommandElements.VariablePath.UserPath) -Value $il.DeclareLocal( [Type]::GetType("System.$($_.CommandElements.Value)") ) } } if (($lnum = ($Code.Split("`n") | Select-String -Pattern '^(\s+)?:').Length)) { $labels = (0..($lnum - 1)).ForEach{ $Code = $Code -replace ":L_$_.*", "`$labels[$_]" $il.DefineLabel() } } [ScriptBlock]::Create((Out-String -InputObject ($Code.Split("`n").Trim().ForEach{ '$il.' + $($_.StartsWith('$') ? "MarkLabel($_)" : "Emit([OpCodes]::$_)") }))).Invoke() $fnarg, $fnret = ($Parameters.Name.ForEach{"[$_]"} -join ', '), "[$($ReturnType.Name)]" $dm.CreateDelegate([void] -eq $ReturnType ? "Action[$fnarg]" : "Func[$( $fnarg ? $fnarg + ', ' + $fnret : $fnret )]" ) } } |