使用Mono.Cecil注入构造方法调用触发InvalidProgramException异常
问题描述
使用Mono.Cecil向构造函数注入方法调用时,抛出以下错误:
InvalidProgramException: Invalid IL code in Networking.ServerController:.ctor (): IL_0028: callvirt 0x0600017f
操作逻辑:针对每个stfld指令,注入父类NetworkBehaviour中的LocalNetworkVariableChange方法调用,传入字段名和装箱后的字段值。该方法定义:
protected void LocalNetworkVariableChange(string variableName, object value) { // 无论此处编写什么代码都会失败 }
注入前构造函数IL:
IL_0000: ldarg.0 IL_0001: newobj System.Void System.Collections.Generic.Dictionary`2<System.UInt16,Networking.ServerController/ConnectedPlayer>::.ctor() IL_0006: stfld System.Collections.Generic.Dictionary`2<System.UInt16,Networking.ServerController/ConnectedPlayer> Networking.ServerController::_connectedPlayersByClientId IL_000b: ldarg.0 IL_000c: newobj System.Void System.Collections.Generic.Dictionary`2<Util.NuID,Networking.ServerController/ConnectedPlayer>::.ctor() IL_0011: stfld System.Collections.Generic.Dictionary`2<Util.NuID,Networking.ServerController/ConnectedPlayer> Networking.ServerController::_connectedPlayersByPlayerId IL_0016: ldarg.0 IL_0017: call System.Void Networking.PeerController`1<Networking.ServerController>::.ctor() IL_001c: nop IL_001d: ret
最终生成的IL代码:
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 62 (0x3e) .maxstack 8 IL_0000: ldarg.0 IL_0001: newobj instance void [netstandard]System.Collections.Generic.Dictionary`2<uint16,class Networking.ServerController/ConnectedPlayer>::.ctor() IL_0006: stfld [netstandard]System.Collections.Generic.Dictionary`2<uint16,class Networking.ServerController/ConnectedPlayer> Networking.ServerController::_connectedPlayersByClientId IL_000b: ldarg.0 IL_000c: newobj instance void [netstandard]System.Collections.Generic.Dictionary`2<class Util.NuID,class Networking.ServerController/ConnectedPlayer>::.ctor() IL_0011: stfld [netstandard]System.Collections.Generic.Dictionary`2<class Util.NuID,class Networking.ServerController/ConnectedPlayer> Networking.ServerController::_connectedPlayersByPlayerId IL_0016: ldarg.0 IL_0017: call instance void class Networking.PeerController`1<class Networking.ServerController>::.ctor() IL_001c: nop IL_001d: ldarg.0 IL_001e: ldstr "_connectedPlayersByClientId" IL_0023: ldfld [netstandard]System.Collections.Generic.Dictionary`2<uint16,class Networking.ServerController/ConnectedPlayer> Networking.ServerController::_connectedPlayersByClientId IL_0028: callvirt instance void Networking.NetworkBehaviour::LocalNetworkVariableChange(string, object) IL_002d: ldarg.0 IL_002e: ldstr "_connectedPlayersByPlayerId" IL_0033: ldfld [netstandard]System.Collections.Generic.Dictionary`2<class Util.NuID,class Networking.ServerController/ConnectedPlayer> Networking.ServerController::_connectedPlayersByPlayerId IL_0038: callvirt instance void Networking.NetworkBehaviour::LocalNetworkVariableChange(string, object) IL_003d: ret } // end of method ServerController::.ctor
相关字段定义:
[NetworkVariable(SyncOnChange = false)] private readonly Dictionary<ushort, ConnectedPlayer> _connectedPlayersByClientId = new Dictionary<ushort, ConnectedPlayer>();
[NetworkVariable(SyncOnChange = false)] private readonly Dictionary<NuID, ConnectedPlayer> _connectedPlayersByPlayerId = new Dictionary<NuID, ConnectedPlayer>();
异常原因
构造阶段虚方法表未初始化完成
C#构造函数执行时,子类会先初始化自身字段,再调用父类构造函数。你注入的方法调用虽然在PeerController构造之后,但此时整个对象的虚方法表(vtable)还未完全初始化。callvirt指令依赖vtable查找方法实现,构造过程中对象状态不稳定,导致指令无法找到有效方法入口,触发异常。错误使用
callvirt指令LocalNetworkVariableChange是父类的非虚方法(未标记virtual/override),不需要虚调度。callvirt会先检查对象引用有效性并通过vtable查找方法,而构造阶段的对象状态无法通过这种检查。应该用call指令直接调用父类方法,跳过虚方法表查找。
解决方法
将注入的callvirt指令替换为call指令:
// 替换前 IL_0028: callvirt instance void Networking.NetworkBehaviour::LocalNetworkVariableChange(string, object) // 替换后 IL_0028: call instance void Networking.NetworkBehaviour::LocalNetworkVariableChange(string, object)
同时保持注入代码在父类构造函数执行之后的位置,确保父类基础逻辑已完成。
内容的提问来源于stack exchange,提问作者Sebastian Barth
相关产品推荐
相关产品推荐

