I want to replace a method with dynamic method in .Net Framework 4, then I found a very useful answer in Dynamically replace the contents of a C# method?, but I can't get MethodHandle from DynamicMethod directly:
we cannot return a MethodHandle because we cannot track it via GC so this method is off limits
In this article CLR Injection: Runtime Method Replacer,
private static IntPtr GetDynamicMethodRuntimeHandle(MethodBase method)
{
if (method is DynamicMethod)
{
FieldInfo fieldInfo = typeof(DynamicMethod).GetField("m_method",
BindingFlags.NonPublic|BindingFlags.Instance);
return ((RuntimeMethodHandle)fieldInfo.GetValue(method)).Value;
}
return method.MethodHandle.Value;
}
which m_method
can not be found.
Then I noticed m_methodHandle
, but don't know when it will be initialized.
internal unsafe RuntimeMethodHandle GetMethodDescriptor() {
if (m_methodHandle == null) {
lock (this) {
if (m_methodHandle == null) {
if (m_DynamicILInfo != null)
m_DynamicILInfo.GetCallableMethod(m_module, this);
else {
if (m_ilGenerator == null || m_ilGenerator.ILOffset == 0)
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_BadEmptyMethodBody", Name));
m_ilGenerator.GetCallableMethod(m_module, this);
}
}
}
}
return new RuntimeMethodHandle(m_methodHandle);
}
According to another question Resolving the tokens found in the IL from a dynamic method, DynamicResolver
has a ResolveToken
method which return methodHandle
address. So I use some code in the answer:
var resolver = typeof(DynamicMethod)
.GetField("m_resolver", BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(dynamicMethod);
if (resolver == null)
throw new ArgumentException("The dynamic method's IL has not been finalized.");
But... DynamicResolver
will only be initialized in DynamicILGenerator.GetCallableMethod
method which will be called in DynamicMethod.GetMethodDescriptor
method, so the resolver
must be null when I getting it.
Here is my dynamic method:
private static MethodInfo build(MethodInfo originMethod)
{
var parameters = originMethod.GetParameters();
var parameterTypes = parameters.Length == 0 ?
null :
parameters
.Select(param => param.ParameterType)
.ToArray();
DynamicMethod method = new DynamicMethod(
originMethod.Name,
originMethod.ReturnType,
parameterTypes,
originMethod.Module);
ILGenerator il = method.GetILGenerator();
il.Emit(OpCodes.Ldstr, "Injected");
var console_writeline = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) });
il.Emit(OpCodes.Call, console_writeline);
il.Emit(OpCodes.Ret);
return method;
}
I learned JIT a very little, so I don't quite understand it.
Can someone help?
--------------------------------Edited--------------------------
the answer of @Latency works fine:
RuntimeMethodHandle GetMethodRuntimeHandle(MethodBase method)
{
if (!(method is DynamicMethod))
return method.MethodHandle;
RuntimeMethodHandle handle;
if (Environment.Version.Major == 4)
{
var getMethodDescriptorInfo = typeof(DynamicMethod).GetMethod("GetMethodDescriptor", BindingFlags.NonPublic | BindingFlags.Instance);
handle = (RuntimeMethodHandle)getMethodDescriptorInfo.Invoke(method, null);
}
else
{
var fieldInfo = typeof(DynamicMethod).GetField("m_method", BindingFlags.NonPublic | BindingFlags.Instance);
handle = (RuntimeMethodHandle)fieldInfo.GetValue(method);
}
return handle;
}
and after so long I can't remember what's next after get RuntimeMethodHandle and reject a dynamic method, but I hope this can help other people.
lol @ lock(this)
The .NET Framework changed its memory specs after v3.5
You will need to put in the condition to test against the framework version.
Typically, you would want to overload the method by doing something like this:
However, this operation is not supported for dynamic methods.
Getting the base definition appears to work.
Which can be simplified to this: