Assume you have a simple class ClassA
in AssemblyA
that should be extended using Mono.Cecil to call a method on its super class ClassB
that is part of another assembly AssemblyB
.
In fact, I am trying to call MonoBehaviour::GetComponent
from my own script in a Unity project. MonoBehaviour is in UnityEngine.CoreModule
assembly
This is my code:
var processor = hookMethod.Body.GetILProcessor();
// find "GetComponent" in BaseType (which is MonoBehaviour)
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);
Unfortunately, I get the following message:
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)
After some research I found that I need to import a reference to the base type from that other assemblie's module.:
var methodRef = typeDefinition.BaseType.Module.ImportReference(typeDefinition.BaseType).Resolve();
var getComponentMethod = methodRef.Methods.First(it => it.Name == "GetComponent");
This results in the exact same error message from above.
Any ideas?
Turns out the trick is to add the .dll-File where the required module is to the search path. This way resolving a type from this module is possible:
using (var asmResolver = new DefaultAssemblyResolver())
using (var assemblyDefinition = AssemblyDefinition.ReadAssembly(assemblyPath, new ReaderParameters { ReadWrite = true, ReadSymbols = true, AssemblyResolver = asmResolver }))
{
asmResolver.AddSearchDirectory(PATH_TO_SEARCH_DIRECTORY);
// do your logic here
}
In my case I needed the UnityEngine.CodeModule
assembly. You can place a path to the containing directory there or you can search it by looping over all assemblies (do it once and store the path because might take a while):
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
var core = assemblies.FirstOrDefault(it => it.FullName.Contains("UnityEngine.CoreModule"));
if (core == null)
throw new ArgumentException("Cannot load unity core dll");
var directoryName = Path.GetDirectoryName(core.CodeBase);
var searchPath = directoryName?.Replace(@"file:\", "");
After doing so you can resolve any type that is contained in any .dll-file from any directory of your search path:
var typeDefinition = moduleDefinition.ImportReference(typeof(ClassFromAnotherModule)).Resolve();