.net - PowerShell - 我如何 Reflection.Emit 正确的 IL OpCodes 来调用 DynamicMethod 中的 [String]::Join?

标签 .net powershell reflection.emit

我一直在尝试使用 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> .

关于.net - PowerShell - 我如何 Reflection.Emit 正确的 IL OpCodes 来调用 DynamicMethod 中的 [String]::Join?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53889841/

相关文章:

C# .NET 3.5 图形用户界面设计

c# - CLR 2.0 与 4.0 的性能对比?

powershell - 如何通过 api 获取 Azure Devops/TFS 中特定构建中使用的变量

string - 使用 PowerShell sls (Select-String) vs grep vs findstr

c# - 使用 Reflection.Emit 以编程方式为现有类属性添加新属性

.NET 4.0 : How can I get the underlying type of a generic parameter if it is Nullable?

c# - C# 中的自动属性是什么,它们的用途是什么?

.net - 无法加载 Exchange powershell 管理单元 : The type initializer for 'Microsoft.Exchange.Data.Directory.Globals' threw an exception

c# - LINQ 表达式和这个

c# - IL 使用 Reflection.Emit 调用带有 params object[] 参数的方法