Reputation: 637
I have the following code that basically creates a DynamicAssembly with 2 types with one public method each.
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly
(new AssemblyName("TestAssembly"), AssemblyBuilderAccess.Run);
var module = assembly.DefineDynamicModule("Main");
var type1 = module.DefineType("type1");
var method1 = type1.DefineMethod
(
"Method1", MethodAttributes.Public, typeof(void), null
);
var gen = method1.GetILGenerator();
gen.Emit(OpCodes.Ret);
var t1 = type1.CreateType();
var createdMethod1 = t1.GetMethod("Method1");
var type2 = module.DefineType("type2");
var method2 = type2.DefineMethod
(
"Method2", MethodAttributes.Public, typeof(void), null
);
byte[] ilCodes = new byte[5];
ilCodes[0] = (byte)OpCodes.Jmp.Value;
ilCodes[1] = (byte)(createdMethod1.MetadataToken & 0xFF);
ilCodes[2] = (byte)(createdMethod1.MetadataToken >> 8 & 0xFF);
ilCodes[3] = (byte)(createdMethod1.MetadataToken >> 16 & 0xFF);
ilCodes[4] = (byte)(createdMethod1.MetadataToken >> 24 & 0xFF);
method2.SetMethodBody(ilCodes, ilCodes.Length, null, null, null);
var obj = Activator.CreateInstance(type2.CreateType());
obj.GetType().GetMethod("Method2").Invoke(obj, null);
Whenever I make a call to type2.method2() I have a JMP instruction to type1.method1().
This works like a charm, both types reside in the same assembly.
Now if I want to redirect to a type that is in another assembly how can I get the correct assembly/module reference in order the JMP instruction to succeed. If I just do like this:
byte[] ilCodes = new byte[5];
ilCodes[0] = (byte)OpCodes.Jmp.Value;
ilCodes[1] = (byte)(methodFromOtherAssembly.MetadataToken & 0xFF);
ilCodes[2] = (byte)(methodFromOtherAssembly.MetadataToken >> 8 & 0xFF);
ilCodes[3] = (byte)(methodFromOtherAssembly.MetadataToken >> 16 & 0xFF);
ilCodes[4] = (byte)(methodFromOtherAssembly.MetadataToken >> 24 & 0xFF);
It keeps failing with IndexNotFound exception.
I want to do this with Raw IL byte instructions.
Upvotes: 3
Views: 278
Reputation: 40818
Metadata tokens are not globally unique. They are resolved in the context of the calling method's module. You need to use GetMetadataToken on the module to get the proper token. Your first example works because the methods share the same module.
This will get the proper token from the dynamic module:
byte[] ilCodes = new byte[5];
int token = module.GetMetadataToken(methodFromOtherAssembly).Token;
ilCodes[0] = (byte)OpCodes.Jmp.Value;
ilCodes[1] = (byte)(token & 0xFF);
ilCodes[2] = (byte)(token >> 8 & 0xFF);
ilCodes[3] = (byte)(token >> 16 & 0xFF);
ilCodes[4] = (byte)(token >> 24 & 0xFF);
Upvotes: 6