C#-使用Harmony重写Dll文件

一. Harmony工作原理

     利用C#运行时Runtime的反射机制,动态加载dll中的方法,字段,属性,实现对DLL方法的重写和代码注入。

二. Harmony下载及csproj文件引用Harmony库

     1.下载Harmony_lib库lib.harmony.2.3.3.nupkg

Harmony API (pardeike.net)https://harmony.pardeike.net/api/index.htmlNuGet Gallery | Lib.Harmony 2.3.3https://www.nuget.org/packages/Lib.Harmony

     2.csproj添加reference引用

<Reference Include="0Harmony">
    <HintPath>..\..\Tool\C#\lib.harmony.2.3.3\lib\net48\0Harmony.dll</HintPath>
</Reference>

三. 激活所有HarmonyPatch

     扫描所有类,如果含有HarmonyPatch注解,则该类实现的Patch将会被激活

var harmonyPatch = new Harmony("patch");
harmonyPatch.PatchAll();

四. 实现目标方法HarmonyPatch

     <1.构造方法

[HarmonyPatch(typeof(OriginalClass), MethodType.Constructor)]
public class MyHarmonyPatch
{
}

     <2.字段内置Setter/Getter方法

[HarmonyPatch(typeof(OriginalClass), "IsLoaded", MethodType.Getter)]
public class MyHarmonyPatch
{
}

     <3.静态方法&实例方法

[HarmonyPatch(typeof(OriginalClass), "OriginalMethod")]
public class MyHarmonyPatch
{
}

     <4.重载方法

[HarmonyPatch(typeof(OriginalClass), "OriginalMethod", new Type[]{})]
public class MyHarmonyPatch
{
}

[HarmonyPatch(typeof(OriginalClass), "OriginalMethod", new Type[1]{typeof(string)})]
public class MyHarmonyPatch
{
}

五. HarmonyPatch拦截

     通过反射创建目标类的代理,可实现对方法调用前,方法调用后,方法异常等进行拦截

     <1.Harmony拦截原理

          通过C#运行时CIR环境中,通过动态反射创建目标类的代理类delegate,在原方法调用时调用delegate指定的方法.

          Harmony实现了前置拦截,后置拦截和异常拦截

     <2.Harmony约定代理参数格式

          目标类实例instance:

                 入参类型: 类类型

                 入参变量名: __instance(固定为__instance)

          目标类实例私有字段field:

                 入参类型: 字段类型

                 入参变量名: ___param(___ + 原私有字段变量名)

          目标方法入参param:

                 入参类型: 参数类型

                 入参变量名: ref arg(ref标识 + 原方法变量名)

          目标方法返回值result:

                 入参类型: object

                 入参变量名: ref __result(固定为__result)

     <3.Prefix前置(在方法调用前调用)

[HarmonyPatch(typeof(OriginalClass), "IsLoaded")]
public class MyHarmonyPatch
{
    public static bool Prefix(object __instance, object __result)
    {
        //__instance调用者实例,可使用后续AccessTool获取该实例相关信息
        __instance.pro
        //修改原方法的返回值
        __result = 2
        //返回True 原方法继续运行 False 原方法不执行
        return true;
    }
}

      <4.Postfix:后置插桩(在方法调用后调用)

[HarmonyPatch(typeof(OriginalClass), "IsLoaded")]
public class MyHarmonyPatch
{
    public static void Postfix(object __instance, object __result)
    {
        //__instance调用者实例,可使用后续AccessTool获取该实例相关信息
        __instance.pro
        //修改原方法的返回值
        __result = 2
    }
}

     <5.Finalizer:异常捕获(在方法发生异常时调用)

[HarmonyPatch(typeof(OriginalClass), "IsLoaded")]
public class MyHarmonyPatch
{
    public static void Finalizer(Exception __exception)
    {
       if (__exception == null)
       {
          return;
       }
    }
}

六. Assembly

     使用Assembly访问私有/受保护的类

Type internalClassType = Assembly.Load("test").GetType("test.test");

七. Traverse

      使用Traverse访问类的私有/受保护的字段&方法

     <1.获取私有/受保护的字段

FieldInfo fieldInfo = Traverse.Create(originalClass).Field("a");

     <2.获取私有/受保护的方法

八. AccessTool

     使用AccessTool访问类的私有/受保护的字段&方法

     <1.获取私有/受保护的字段

FieldInfo info = AccessTools.Field(typeof(OriginalClass), "b");

     <2.获取私有/受保护的方法

          1.构造方法

ConstructorInfo method = AccessTools.Constructor(typeof(OriginalClass), new Type[0]{});

          2.字段内置Setter/Getter方法

MethodInfo isLoadedSetter = AccessTools.PropertySetter(typeof(OriginalClass), "IsLoaded");

         3.静态方法&实例方法

MethodInfo method = AccessTools.Method(typeof(OriginalClass), "OtherMethod");

九. MethodInfo

     获取私有/受保护方法后会得到MethodInfo实例

     <1.对方法进行调用

         1.调用静态方法

method.Invoke(null, new object[0]);

方法中含有ref, in, out等引用传递时,通过将参数值设为null,调用后取出
原方法: void AddMusic(int a, int b, out string c)
object[] parameters = new object[]{1, 2, null}
method.Invoke(null, parameters);
string c = parameters[2]

         2.调用实例方法&字段内置Setter/Getter方法

method.Invoke(__instance, new object[0]);

方法中含有ref, in, out等引用传递时,通过将参数值设为null,调用后取出
原方法: void AddMusic(int a, int b, out string c)
object[] parameters = new object[]{1, 2, null}
method.Invoke(null, parameters);
string c = parameters[2]

         3.调用构造方法

method.Invoke(new object[0]);

方法中含有ref, in, out等引用传递时,通过将参数值设为null,调用后取出
原方法: void AddMusic(int a, int b, out string c)
object[] parameters = new object[]{1, 2, null}
method.Invoke(null, parameters);
string c = parameters[2]

     <2.方法转化为Action/Func代理

         1.获取Action代理

var delegate = methodInfo.CreateDelegate(typeof(Action), __instance);

Action<Test> action = (Action<Test>)(object)delegate

         2.获取Func代理

var delegate = methodInfo.CreateDelegate(typeof(Func<PlayMusic>), __instance);

Func<PlayMusic> func = (Func<PlayMusic>)(object)delegate;

十. FieldInfo

     获取私有/受保护字段后会得到FieldInfo实例

     <1.获取字段值

int value = fieldInfo.GetValue<int>()

     <2.设置字段值

int value = fieldInfo.SetValue<int>()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

霸王奉先

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值