如何在 MSIL 中调用执行程序集外部的方法?

How can I call a method outside the executing assembly in MSIL?

我可以访问这样的函数体中间语言:

byte[] ilCodes = NestedFooInfo.GetMethodBody().GetILAsByteArray();

我能够修改其 IL 代码,以便在执行方法主体之前调用以下名为 OnChangeField:

的方法
public static void OnChangeField()
{
    Console.WriteLine("VICTORY");
    return;
}

到目前为止我是这样做的:

我定义调用指令到我要调用的方法:

MethodInfo OnStfld = typeof(MethodBoundaryAspect).GetMethod("OnChangeField");
byte[] callIL = new byte[5];
callIL[0] = (byte)OpCodes.Call.Value;
callIL[1] = (byte)(OnStfld.MetadataToken & 0xFF);
callIL[2] = (byte)(OnStfld.MetadataToken >> 8 & 0xFF);
callIL[3] = (byte)(OnStfld.MetadataToken >> 16 & 0xFF);
callIL[4] = (byte)(OnStfld.MetadataToken >> 24 & 0xFF);

请注意 OnChangeField 位于 MethodBoundaryAspect Class 中。此 class 位于正在编辑的方法的程序集之外。

这就是我改变原始(NestedFoo(...))方法主体的方式:

byte[] ilCodes = NestedFooInfo.GetMethodBody().GetILAsByteArray();
InjectionHelper.UpdateILCodes(NestedFooInfo, callIL.Concat(ilCodes).ToArray());

然后我得到一个:

System.BadImageFormatException: 'Index not found. (Exception from HRESULT: 0x80131124)'

但是 只有 如果挂钩方法 OnChangeField 在执行程序集之外(或者据我所知)。如果我将 OnChangeField 移动到与 NestedFoo 相同的 class 或 NestedFoo 程序集的另一个 class 中,它可以完美运行。

我知道元数据令牌指向一个无效的内存位置。有办法改变吗?

这里的参考资料是: 更改后的方法的方法体是什么样的:

public class ExceptionHandlingService : IExceptionHandlingService
{
        public static string var1 = "initialValue";
        public static string Var2 { get; set; } = "initialValue";
        public string var3 = "initialValue";
        public string Var4 { get; set; } = "initialValue";

        public string NestedFoo(SampleClass bar)
        {
            var1 = "value set in NestedFoo()";
            Var2 = "value set in NestedFoo()";
            var3 = "value set in NestedFoo()";
            Var4 = "value set in NestedFoo()";
            AddPerson("From", "NestedFoo", 2);
            return Foo();
        }
        [...]
}

以及我如何调用更改后的方法:

var a = new ExceptionHandlingService();
var b = new SampleClass("bonjour", 2, 3L); // Not really relevant
a.NestedFoo(b);

对于那些想知道魔法发生了什么的人 InjectionHelper.UpdateILCodes(NestedFooInfo, newIlCodes); 您可以查看 this link,其中显示了如何在运行时编辑 Il 代码。

您需要将记录添加到 MemberRef 并连续添加 TypeRefTypeSpec 元数据表以包含对类型的引用并引用这些标记。这还涉及正确编写签名 blob。

参见 ECMA-335

的第二部分 22.38、22.25