如何获取堆栈帧的执行对象?

Cec*_*ame 11 c# reflection stack-trace stack-frame

使用反射时,可以使用System.Diagnostics.StackTrace获取调用堆栈(除了它可以是由JIT优化引起的粗略近似)并检查包含的StackFrame对象.

如何获取对堆栈帧中的方法正在执行的对象(this-pointer)的引用?

我知道我可以通过在堆栈框架对象上调用GetMethod()来获取MethodBase,但我正在寻找的东西是GetObject()(如果方法是静态的,它会自然地返回null).看起来堆栈帧对象只能查询静态确定的信息,例如方法信息,原始文件等.

VS调试器知道(虽然它可能使用另一种获取调用堆栈跟踪的方法),因为可以双击调用堆栈窗口中的任何堆栈帧并查看本地和类字段的值.

编辑:澄清:我想要调用该方法的对象实例.即:如果在调用堆栈的某个地方的对象实例A上调用方法Foo(),并且它级联到我执行堆栈跟踪的方法,我想从我执行堆栈跟踪的位置获取对A的引用.(不是方法库的声明类型)

Yon*_*ona 6

我很确定这是不可能的.原因如下:

  1. 这可能会破坏类型安全性,因为任何人都可以查找一个框架,无论他们正在执行哪个AppDomain\Thread或他们拥有的权限,都可以获取该对象.

  2. ' this'(C#)标识符实际上只是实例方法(第一个)的参数,所以实际上静态方法和实例方法之间没有区别,编译器会将权限传递this给实例方法,当然意味着您需要访问所有方法参数才能获取该this对象.(StackFrame不支持)

有可能通过使用unsafe代码将第一个参数的指针获取到实例方法然后将其转换为正确的类型,但我不知道如何做到这一点,只是一个想法.

顺便说一下,你可以想象实例方法在被编译成C#3.0扩展方法之后,它们将this指针作为它们的第一个参数.


小智 5

可以获取对thiscall对象的引用,但不能仅使用 .NET 代码。必须涉及到原生代码。即使使用动态类和 System.Relection.Emit 命名空间,.NET 也没有工具来访问其他方法评估堆栈和参数。

另一方面,如果反汇编 .NET 方法,您会发现该引用根本没有在物理堆栈上传递。Thiscall引用存储在ECXRCX对于 x64)寄存器中。因此,可以在堆栈上从您的方法爬升到您想要从中获取thiscall对象的方法。然后在该方法的机器代码内部查找将寄存器 ECX (RCX) 保存在堆栈中的指令,并从该指令获取该引用所在的相对地址。

当然,在x32和x64应用中,爬升的方法是有很大不同的。要生成这样的函数,您不仅必须使用 C#,还必须使用汇编代码,并记住 x64 下不允许使用内联汇编程序;它必须是一个完整的汇编器模块。