我一直在尝试使用 PowerShell 将 CIL 发射到 DynamicMethod 中,然后运行它,并且基本操作有效,所以我相信该方法是可行的。我可以将 int 和 string 值压入堆栈并打印它们,这涉及找到 [console]::WriteLine([string])[console]::WriteLine( [int32]) - 即获取所有重载并按其参数类型进行过滤。

现在我正在尝试调用 [String]::Join() 但这是一个通用方法,需要键入以加入 [int32[]]。 C# 反汇编表明 CIL 指令是:

IL_0018:  ldstr      ", " 
IL_001d:  ldloc.2 
IL_001e:  call       string [mscorlib]System.String::Join<int32>(string, 
                               class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>) 
IL_0023:  stloc.3 
IL_0024:  ldloc.3 
IL_0025:  call       void [mscorlib]System.Console::WriteLine(string) 

那是插入分隔符,加载数组引用,调用连接,(存储/加载结果字符串),调用 WriteLine。

我的 PowerShell(很多代码,因为它在 CIL 中设置数组,但它尽可能小,我可以制作一个完整的可运行示例),如下所示:

using namespace System.Reflection.Emit 
 
# new dynamic method 
$methodInfo = new-object Reflection.Emit.DynamicMethod -ArgumentList @('Test', [int], @()) 
$IL = $methodInfo.GetILGenerator() 
 
# new array of [int32[]] items, stored in a local variable 
$arrayVar = $IL.DeclareLocal([int32[]]) 
$IL.Emit([OpCodes]::Ldc_I4, 2) 
$IL.Emit([OpCodes]::Newarr, [int32]) 
$IL.Emit([OpCodes]::Stloc, $arrayVar) 
 
# Store 4 into index 0 
$IL.Emit([OpCodes]::Ldloc, $arrayVar) 
$IL.Emit([OpCodes]::Ldc_I4, 0) 
$IL.Emit([OpCodes]::Ldc_I4_4) 
$IL.Emit([OpCodes]::Stelem, [int32]) 
 
# Store 5 into index 1 
$IL.Emit([OpCodes]::Ldloc, $arrayVar) 
$IL.Emit([OpCodes]::Ldc_I4, 1) 
$IL.Emit([OpCodes]::Ldc_I4_5) 
$IL.Emit([OpCodes]::Stelem, [int32]) 
 
# 
# *** question here *** 
# 
# The bit I can't get to work - [String]::Join('; ', $array) 
# Push separator string onto the stack, then load the array, then call the Join method 
$IL.Emit([OpCodes]::Ldstr, '; ') 
$IL.Emit([OpCodes]::Ldloc, $arrayVar) 
$IL.EmitCall([opcodes]::Call, [string].GetDeclaredMethods('Join').where{$_.IsGenericMethod}[0].MakeGenericMethod([int32[]]), $null) 
 
# Should leave a string on the stack; print it. 
$IL.EmitCall([OpCodes]::Call, [console].GetDeclaredMethods('WriteLine').where{$p = $_.GetParameters(); $p.Count -eq 1 -and $p.ParameterType -eq [string]}[0], $null) 
 
 
# return 0 
$IL.Emit([OpCodes]::Ldc_I4_0) 
$IL.Emit([OpCodes]::Ret) 
 
# run code 
$Test = $methodInfo.CreateDelegate([System.Func[[int]]]) 
$Test.Invoke() 

它抛出:

Exception calling "Invoke" with "0" argument(s): "Operation could destabilize the runtime." 

如果您注释掉加载数组和调用 Join 的两行代码,那么代码运行良好并且只打印分隔符字符串 ;。所以那部分看起来没问题。我相当确定数组设置代码也可以,但不是绝对确定。它至少没有以错误的方式获取值和索引(抛出索引越界异常)。我发出的 OpCodes 看起来像用于将值存储在数组中的 C# 反汇编。

那么,如在 C# 反汇编中所见,发出对类型为 int32 的 String.Join 调用的正确 PowerShell 代码是什么?

请您参考如下方法:

需要对数组[int]中的项进行打字不是整个数组 [int[]] .更改我的 PowerShell 以删除多余的 []现在有效:

$IL.EmitCall([opcodes]::Call, [string].GetDeclaredMethods('Join').where{ 
    $_.IsGenericMethod 
}[0].MakeGenericMethod([int32]), $null) 

我是怎么找到它的

我用了this article on C-SharpCorner作为将我的 DynamicMethod 保存为 .exe 的指南,然后在其上使用 ILDasm 将生成的代码与 C# 版本进行比较。

我需要这个的地方:

string [mscorlib]System.String::Join<int32>(string, 
                            class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>) 

我得到这个:

string [mscorlib]System.String::Join<int32[]>(string, 
                            class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>) 

特别是 Join<int32[]>而不是 Join<int32> .


评论关闭
IT序号网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!