如何正确使用OpCodes.Call?调用自定义方法触发System.InvalidProgramException
解决DynamicMethod调用实例方法时的System.InvalidProgramException问题
我帮你定位到问题根源了——你遇到的System.InvalidProgramException是因为调用实例方法时IL指令的栈布局错误,缺少了实例对象的引用,导致CLR无法正确执行方法调用。
问题原因拆解
你的Test是Form1的实例方法,调用实例方法必须满足栈布局要求:先加载类的实例(即Form1的this对象),再加载方法的参数。但你当前的IL代码只加载了动态方法的int参数(Ldarg_0)就直接调用Call,栈上只有一个int值,不符合实例方法的调用规范,因此触发了无效程序异常。
另外,你在调用Test后还添加了Dup和Mul指令,这会把Test返回的平方结果再平方一次(计算s^4),如果这不是你的预期,记得去掉这两句。
解决方案
这里提供两种可行的修复方案,你可以根据需求选择:
方案一:修改动态方法,传入Form1实例作为参数
这种方案保留Test的实例方法特性,需要给动态方法增加一个Form1实例参数:
- 修改委托定义,加入Form1实例参数:
private delegate long squareDel(Form1 instance, int a); - 调整DynamicMethod的参数列表,将Form1作为第一个参数:
DynamicMethod dm = new DynamicMethod("", typeof(long), new Type[] { typeof(Form1), typeof(int) }, typeof(Form1)); - 修正IL指令,先加载实例再加载参数,最后调用方法:
MethodInfo mi = typeof(Form1).GetMethod("Test", new Type[] { typeof(int) }); ig.Emit(OpCodes.Ldarg_0); // 加载Form1实例 ig.Emit(OpCodes.Ldarg_1); // 加载int参数 ig.EmitCall(OpCodes.Call, mi, null); ig.Emit(OpCodes.Ret); // Test已经返回s*s,无需额外Dup和Mul - 调用委托时传入当前Form1实例:
MessageBox.Show(test(this, int.Parse(textBox1.Text)).ToString());
方案二:将Test方法改为静态方法
如果Test不需要依赖Form1的实例状态,直接改成静态方法是最简单的方式:
- 修改Test方法为静态:
public static long Test(int s) { return s*s; } - 保留原有委托和DynamicMethod的参数结构,修正IL指令:
MethodInfo mi = typeof(Form1).GetMethod("Test", new Type[] { typeof(int) }); ig.Emit(OpCodes.Ldarg_0); // 加载int参数 ig.EmitCall(OpCodes.Call, mi, null); ig.Emit(OpCodes.Ret);
这种方式不需要改动委托定义和调用逻辑,和你最初的代码结构最接近。
内容的提问来源于stack exchange,提问作者PiggyChu001
相关产品推荐
相关产品推荐

