ILCode编译错误 - C#

时间:2017-06-29 18:30:17

标签: c#

/请跳至更新,

  

将Net Framework 3.5(Mono)与C#7.0一起使用

我尝试创建动态方法,将其参数化,将它们装入对象数组并使用简单方法调用它。

然后在运行时我通过不安全的上下文替换方法。 (为了更好的API而做)

我创建了我想象的代码,并在LINQPad 5中发布了该代码。

void TestCall(string a, int b) {
    NetworkInvoke(a, b);
}

void NetworkInvoke(params object[] parameters) {

}

在LINQPad中我得到了IL代码:

g__TestCall0_0:
IL_0000:  nop         
IL_0001:  ldc.i4.2    
IL_0002:  newarr      System.Object
IL_0007:  dup         
IL_0008:  ldc.i4.0    
IL_0009:  ldarg.0     
IL_000A:  stelem.ref  
IL_000B:  dup         
IL_000C:  ldc.i4.1    
IL_000D:  ldarg.1     
IL_000E:  box         System.Int32
IL_0013:  stelem.ref  
IL_0014:  call        g__NetworkInvoke0_1
IL_0019:  nop         
IL_001A:  ret   

现在让我们在ILGenerator中重新创建它。 (我已经有了支持所有参数的代码,但它也不起作用。然后我创建了这个简单的静态代码,它也不起作用)

private MethodInfo CreateMethodFrom(MethodInfo method) {
    var name = method.Name;
    var attributes = method.Attributes;
    var callConv = method.CallingConvention;
    var returnType = method.ReturnType;
    var parameters = new Type[method.GetParameters().Length];
    for (int i = 0; i < parameters.Length; i++)
        parameters[i] = method.GetParameters()[i].ParameterType;

    Debug.Log("Creating dynamic method!");

    var dynMethod = new DynamicMethod(name, attributes, callConv, returnType, parameters, GetType().BaseType, false);
    for (int i = 0; i < parameters.Length; i++) {
        dynMethod.DefineParameter(i, method.GetParameters()[i].Attributes, method.GetParameters()[i].Name);
    }

    var networkInvoker = GetType().BaseType.GetMethod("NetworkInvoke", BindingFlags.Instance | BindingFlags.NonPublic);

    Debug.Log("Creating ILGenerator!");

    ILGenerator il = dynMethod.GetILGenerator();

    //ILCode(il, parameters, networkInvoker);

    il.Emit(OpCodes.Nop);
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Ldc_I4_2);
    il.Emit(OpCodes.Newarr, typeof(System.Object));
    il.Emit(OpCodes.Dup);
    il.Emit(OpCodes.Ldc_I4_0);
    il.Emit(OpCodes.Ldarg_1);
    il.Emit(OpCodes.Stelem_Ref);
    il.Emit(OpCodes.Dup);
    il.Emit(OpCodes.Ldc_I4_1);
    il.Emit(OpCodes.Ldarg_2);
    il.Emit(OpCodes.Box, typeof(System.Int32));
    il.Emit(OpCodes.Stelem_Ref);
    il.Emit(OpCodes.Call, networkInvoker);
    il.Emit(OpCodes.Nop);
    il.Emit(OpCodes.Ret);

    return dynMethod;
}

让我们调用我们的动态方法:

forEach { //Iterating over methods
    MethodInfo ourCorrectMethodToBeReplaced = it; //This method is for a debug, its signature is: (string testString, int testInt)
    var replacedMethod = CreateMethodFrom(ourCorrectMethodToBeReplaced);

    string test = "test1";
    int test2 = 69;

    replacedMethod.Invoke(methodInstance, new object[] {test, test2});
} 

当我调用它时,我收到InvalidProgramException

  

InvalidProgramException:(包装器动态方法)中的IL代码无效   SioFramework.Script:TestCall(string,int):IL_000b:stelem.ref

我试图删除引用 - (Stelem_Ref)然后我已经

  

InvalidProgramException:(包装器动态方法)中的IL代码无效   SioFramework.Script:TestCall(string,int):IL_000d:ldarg.2

任何想法我做错了什么?谢谢你的任何提示。

更新,好吧,看起来我在这里发布了错误的代码。 (我昨天几乎整整都试图解决这个问题)

当我将ILCode重写为正确的(抱歉,我的错误)时

il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ldc_I4_2);
il.Emit(OpCodes.Newarr, typeof(System.Object));
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Stelem_Ref);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Box, typeof(System.Int32));
il.Emit(OpCodes.Stelem_Ref);
il.Emit(OpCodes.Call, networkInvoker);
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ret);

我有

  

InvalidProgramException:(包装器动态方法)中的IL代码无效   SioFramework.Script:TestCall(string,int):IL_0014:call
  0x00000005

这是正确的错误。

更新2 我创建了Debug,在比较两种方法时看起来都相同。

private void DebugM(MethodInfo method) {
    var builder = new StringBuilder();
    builder.Append("Name: ").Append(method.Name);
    builder.Append("\nParameters(").Append(method.GetParameters().Length).Append("): ");

    foreach (var parameter in method.GetParameters()) {
        builder.Append("(").Append(parameter.ParameterType.FullName).Append(")");
    }
    builder.Append("\nReturnType: ").Append(method.ReturnType.FullName);
    builder.Append("\nMemberType: ").Append(method.MemberType);
    builder.Append("\nCC: ").Append(method.CallingConvention);
    builder.Append("\nModule: ").Append(method.Module.Name);
    Debug.Log(builder.ToString());
}
  

名称:TestCall参数(2):( System.String)(System.Int32)   ReturnType:System.Void MemberType:方法CC:标准,HasThis   模块:Assembly-CSharp.dll

我创建了与正在重建的内容完全相同的方法: 检测是否由于参数计数而导致 正在重建的方法:

public void TestCall(string testString, int testInt) {
    Debug.Log("Called TestCAll with params: " + testInt + ", " + testInt);
}

致电方法:

internal void TestMethod(string a, int b) {
    Debug.Log("Called TestMethod!!");
}

我试图用委托调用它:

public delegate void TestDelegate(String a, int b);

//In end of CreateMethodFrom
var delTest = (TestDelegate) dynMethod.CreateDelegate(typeof(TestDelegate));
delTest("TESTA", 31321321);

结果相同:

  

InvalidProgramException:(包装器动态方法)中的IL代码无效   SioFramework.Script:TestCall(string,int):IL_0014:call
  0x00000005

堆栈跟踪

System.Delegate.CreateDelegate (System.Type type, System.Object firstArgument, System.Reflection.MethodInfo method, Boolean throwOnBindFailure) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System/Delegate.cs:268)
System.Delegate.CreateDelegate (System.Type type, System.Reflection.MethodInfo method, Boolean throwOnBindFailure) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System/Delegate.cs:291)
System.Delegate.CreateDelegate (System.Type type, System.Reflection.MethodInfo method) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System/Delegate.cs:295)
System.Reflection.Emit.DynamicMethod.CreateDelegate (System.Type delegateType) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection.Emit/DynamicMethod.cs:179)
SioFramework.Script.CreateMethodFrom (System.Reflection.MethodInfo method) (at Assets/SioFramework/Script.cs:167)

167:

var delTest = (TestDelegate) dynMethod.CreateDelegate(typeof(TestDelegate));

0 个答案:

没有答案