问题描述
在帖子Replacing C# method of declaring type which implements an interface and inherits from base中,我知道如何将方法指针更改为方法的另一个指针或Action
/ Func
。但是我需要将其替换为指向Dynamicmethod
的指针。
提到的帖子中的工作示例,其中添加了一种方法,稍后将使用该方法检查其是否与Dynamicmethod
一起使用:
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace Foo.Bar {
public interface IFoo {
string Apple();
}
public class Bar {
protected virtual object One() {
return null;
}
protected virtual object Two() {
return null;
}
protected virtual object Three() {
return null;
}
// New
public override int VirtualTestMethod()
{
return 10;
}
/* Uncomment this to generate a null reference */
//protected virtual object Four() {
// return null;
//}
}
public class Foo : Bar,IFoo {
public string Apple() {
return "Apple";
}
public string Orange() {
return "Orange";
}
// New
public override int VirtualTestMethod()
{
return 0;
}
/* Uncommenting this fixes the null reference */
//public override int GetHashCode() {
// throw new NotImplementedException();
//}
public void ReplaceMethod(Delegate targetmethod,Delegate replacementMethod) {
MethodInfo methodToReplace = targetmethod.Method;
MethodInfo methodToInject = replacementMethod.Method;
RuntimeHelpers.PrepareMethod(methodToReplace.MethodHandle);
RuntimeHelpers.PrepareMethod(methodToInject.MethodHandle);
if (methodToReplace.IsVirtual)
ReplaceVirtualInner(methodToReplace,methodToInject);
else
ReplaceStandard(methodToReplace,methodToInject);
}
// New
public void ReplaceMethod(MethodInfo methodToReplace,MethodInfo methodToInject)
{
RuntimeHelpers.PrepareMethod(methodToReplace.MethodHandle);
RuntimeHelpers.PrepareMethod(methodToInject.MethodHandle);
if (methodToReplace.IsVirtual)
ReplaceVirtualInner(methodToReplace,methodToInject);
}
private void ReplaceStandard(MethodInfo methodToReplace,MethodInfo methodToInject) {
IntPtr targetPtr = methodToInject.MethodHandle.Value;
IntPtr replacePtr = methodToReplace.MethodHandle.Value;
unsafe
{
if (IntPtr.Size == 4) {
int* inj = (int*)replacePtr.ToPointer() + 2;
int* tar = (int*)targetPtr.ToPointer() + 2;
if (Debugger.IsAttached) {
byte* injInst = (byte*)*inj;
byte* tarInst = (byte*)*tar;
int* injsrc = (int*)(injInst + 1);
int* tarSrc = (int*)(tarInst + 1);
*tarSrc = (((int)injInst + 5) + *injsrc) - ((int)tarInst + 5);
}
else {
*tar = *inj;
}
}
else {
long* inj = (long*)replacePtr.ToPointer() + 1;
long* tar = (long*)targetPtr.ToPointer() + 1;
if (Debugger.IsAttached) {
byte* injInst = (byte*)*inj;
byte* tarInst = (byte*)*tar;
long* injsrc = (long*)(injInst + 1);
long* tarSrc = (long*)(tarInst + 1);
*tarSrc = (((long)injInst + 5) + *injsrc) - ((long)tarInst + 5);
}
else {
*tar = *inj;
}
}
}
}
private void ReplaceVirtualInner(MethodInfo methodToReplace,MethodInfo methodToInject) {
unsafe
{
UInt64* methodDesc = (UInt64*)(methodToReplace.MethodHandle.Value.ToPointer());
int index = (int)(((*methodDesc) >> 32) & 0xFF);
if (IntPtr.Size == 4) {
uint* classstart = (uint*)methodToReplace.DeclaringType.TypeHandle.Value.ToPointer();
classstart += 10;
classstart = (uint*)*classstart;
uint* tar = classstart + index;
uint* inj = (uint*)methodToInject.MethodHandle.Value.ToPointer() + 2;
if (Debugger.IsAttached) {
byte* injInst = (byte*)*inj;
byte* tarInst = (byte*)*tar;
uint* injsrc = (uint*)(injInst + 1);
uint* tarSrc = (uint*)(tarInst + 1);
*tarSrc = (((uint)injInst + 5) + *injsrc) - ((uint)tarInst + 5);
}
else {
*tar = *inj;
}
}
else {
ulong* classstart = (ulong*)methodToReplace.DeclaringType.TypeHandle.Value.ToPointer();
classstart += 8;
classstart = (ulong*)*classstart;
ulong* tar = classstart + index;
ulong* inj = (ulong*)methodToInject.MethodHandle.Value.ToPointer() + 1;
if (Debugger.IsAttached) {
byte* injInst = (byte*)*inj;
byte* tarInst = (byte*)*tar;
ulong* injsrc = (ulong*)(injInst + 1);
ulong* tarSrc = (ulong*)(tarInst + 1);
*tarSrc = (((ulong)injInst + 5) + *injsrc) - ((ulong)tarInst + 5);
}
else {
*tar = *inj;
}
}
}
}
}
}
工作用法示例(也可与其他ReplaceMethod
-过载一起使用
Foo.Bar.Foo foo = new Foo.Bar.Foo();
foo.ReplaceMethod(
((Func<string>)foo.Apple),((Func<string>)foo.Orange)
);
var result = foo.Apple(); // this is "Orange" :)
不起作用的示例:
Foo.Bar.Foo foo = new Foo.Bar.Foo();
var methodInfo = foo.GetType().getmethod("VirtualTestMethod",(BindingFlags)(-1));
var dm = new Dynamicmethod("",typeof(int),new Type[]{});
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldc_I4,25);
il.Emit(OpCodes.Ret);
foo.ReplaceMethod(
methodInfo,dm.Method
);
var result = foo.VirtualTestMethod(); // this should be 25
但是,如果我尝试将其替换为Dynamicmethod
,那么它将不起作用,因为它没有MethodHandle
。如果我使用以下代码片段从MethodHandle
获取Dynamicmethod
并将其用于方法中,则该方法将无效。摘录摘自该SO answer。
private static readonly Func<Dynamicmethod,RuntimeMethodHandle> getDynamicHandle = Delegate.CreateDelegate(
typeof(Func<Dynamicmethod,RuntimeMethodHandle>),typeof(Dynamicmethod).getmethod("getmethodDescriptor",BindingFlags.Instance | BindingFlags.NonPublic)
) as Func<Dynamicmethod,RuntimeMethodHandle>;
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)