C#中用Mono.Cecil调用其他程序集父类方法的问题(Unity场景)
解决Unity中Mono.Cecil无法解析UnityEngine.CoreModule的问题
问题场景
在Unity项目中,使用Mono.Cecil对自定义脚本(继承自MonoBehaviour)做IL注入,尝试调用父类MonoBehaviour的GetComponent方法时,运行时抛出AssemblyResolutionException,提示无法解析UnityEngine.CoreModule程序集。
初始实现代码
var processor = hookMethod.Body.GetILProcessor(); // 查找基类(MonoBehaviour)中的GetComponent方法 var getComponentMethod = typeDefinition.BaseType.Resolve().Methods.First(it => it.Name == "GetComponent"); var firstInstruction = hookMethod.Body.Instructions[0]; var thisCall = processor.Create(OpCodes.Ldarg_0); var methodCall = processor.Create(OpCodes.Call, getComponentMethod); processor.InsertBefore(firstInstruction, methodCall); processor.InsertBefore(methodCall, thisCall);
抛出的错误信息
AssemblyResolutionException: Failed to resolve assembly: 'UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' Mono.Cecil.BaseAssemblyResolver.Resolve (Mono.Cecil.AssemblyNameReference name, Mono.Cecil.ReaderParameters parameters) (at <6f6cad7a41144167841153769a3850ff>:0) Mono.Cecil.BaseAssemblyResolver.Resolve (Mono.Cecil.AssemblyNameReference name) (at <6f6cad7a41144167841153769a3850ff>:0) Mono.Cecil.DefaultAssemblyResolver.Resolve (Mono.Cecil.AssemblyNameReference name) (at <6f6cad7a41144167841153769a3850ff>:0) Mono.Cecil.MetadataResolver.Resolve (Mono.Cecil.TypeReference type) (at <6f6cad7a41144167841153769a3850ff>:0) Mono.Cecil.ModuleDefinition.Resolve (Mono.Cecil.TypeReference type) (at <6f6cad7a41144167841153769a3850ff>:0) Mono.Cecil.TypeReference.Resolve () (at <6f6cad7a41144167841153769a3850ff>:0) MissingBracket.Injection.Editor.InjectionProcessor.Process (Mono.Cecil.TypeDefinition typeDefinition, Mono.Cecil.ModuleDefinition moduleDefinition, System.Boolean& modified) (at Assets/Plugins/Missing Bracket/Injection/Editor/InjectionProcessor.cs:33) MissingBracket.Common.Editor.Weaver.Weave (System.Reflection.Assembly[] assemblies, Mono.Cecil.AssemblyDefinition assemblyDefinition, Mono.Cecil.IAssemblyResolver asmResolver, System.Boolean& modified) (at Assets/Plugins/Missing Bracket/Common/Editor/Weaver/Weaver.cs:35) MissingBracket.Common.Editor.WeaverExecutor.WeaveFromFile (System.String assemblyPath) (at Assets/Plugins/Missing Bracket/Common/Editor/Weaver/WeaverExecutor.cs:52) MissingBracket.Common.Editor.WeaverExecutor.OnCompilationFinished (System.String assemblyPath, UnityEditor.Compilation.CompilerMessage[] messages) (at Assets/Plugins/Missing Bracket/Common/Editor/Weaver/WeaverExecutor.cs:40) UnityEditor.Compilation.CompilationPipeline+<>c.<SubscribeToEvents>b__26_3 (UnityEditor.Scripting.ScriptCompilation.ScriptAssembly scriptAssembly, UnityEditor.Compilation.CompilerMessage[] messages) (at <97436df440ca462884c5332c1d8ebbe7>:0) UnityEditor.Scripting.ScriptCompilation.EditorCompilationInterface:TickCompilationPipeline(EditorScriptCompilationOptions, BuildTargetGroup, BuildTarget, Int32, String[], Boolean)
尝试过的无效代码
var methodRef = typeDefinition.BaseType.Module.ImportReference(typeDefinition.BaseType).Resolve(); var getComponentMethod = methodRef.Methods.First(it => it.Name == "GetComponent");
解决方案
错误根源是Mono.Cecil的默认程序集解析器不知道Unity核心程序集的存放路径,需要手动配置解析器的搜索目录,同时修正IL指令的调用方式。
步骤1:配置程序集解析器
在加载目标程序集之前,给DefaultAssemblyResolver添加Unity引擎程序集的搜索路径:
var resolver = new DefaultAssemblyResolver(); // 添加Unity核心程序集目录(根据Unity版本调整路径) var unityManagedPath = Path.Combine(EditorApplication.applicationContentsPath, "Managed"); resolver.AddSearchDirectory(unityManagedPath); // 部分Unity版本需要额外添加子目录 var unityEnginePath = Path.Combine(unityManagedPath, "UnityEngine"); resolver.AddSearchDirectory(unityEnginePath); // 加载程序集时传入配置好的解析器 var readerParams = new ReaderParameters { AssemblyResolver = resolver }; var assemblyDefinition = AssemblyDefinition.ReadAssembly(assemblyPath, readerParams);
步骤2:正确引用并调用目标方法
避免不必要的程序集解析,同时注意虚方法的调用指令:
var processor = hookMethod.Body.GetILProcessor(); // 获取MonoBehaviour类型引用 var baseTypeRef = typeDefinition.BaseType; // 匹配正确的GetComponent重载(这里选非泛型、接收System.Type参数的版本) var getComponentMethodDef = baseTypeRef.Resolve().Methods.First( m => m.Name == "GetComponent" && m.Parameters.Count == 1 && m.Parameters[0].ParameterType.FullName == "System.Type"); // 将方法定义导入到当前模块 var getComponentMethodRef = typeDefinition.Module.ImportReference(getComponentMethodDef); var firstInstruction = hookMethod.Body.Instructions[0]; // 构建IL指令:先加载this,再调用虚方法 var ldarg0 = processor.Create(OpCodes.Ldarg_0); // 注意:MonoBehaviour.GetComponent是虚方法,必须用Callvirt而非Call var callMethod = processor.Create(OpCodes.Callvirt, getComponentMethodRef); processor.InsertBefore(firstInstruction, callMethod); processor.InsertBefore(callMethod, ldarg0);
关键注意事项
- 虚方法调用:
GetComponent是虚方法,必须使用OpCodes.Callvirt,否则会触发运行时错误。 - 重载匹配:
GetComponent有多个重载(泛型、非泛型),要根据实际需求筛选对应签名的方法。 - 路径适配:不同Unity版本的程序集存放路径可能有差异,需根据项目使用的版本调整搜索目录。
内容的提问来源于stack exchange,提问作者FeXseven
相关产品推荐
相关产品推荐

