微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何使用 Harmony 重复替换任意方法?

如何解决如何使用 Harmony 重复替换任意方法?

我需要使用对具有相同签名的方法调用来替换方法,这样我基本上可以用新方法替换原始方法。目前,我有下面的代码,它可以工作,但是当我再次尝试修补该方法时,它什么也不做。我不确定这是不是因为 Harmony 不喜欢我尝试对其进行两次转换或其他原因,无论哪种方式都会阻止我重复重定向原始方法

// this is factored out of transpiler() because yield return reasons
private static IEnumerable<CodeInstruction> transpilerIterator(IEnumerable<CodeInstruction> instructions,MethodBase original) {
    var name = original.Name;
    var par = original.GetParameters();
    var method = newGuiType.getmethod(name,(BindingFlags) FLAGS);
    Console.WriteLine($"{name} == null == {method == null}");

    if ((method.CallingConvention & CallingConventions.HasThis) != 0)
        yield return new CodeInstruction(OpCodes.Ldarg_0);

    for (var i = 0; i < par.Length; i++)
        yield return new CodeInstruction(OpCodes.Ldarg_S,par[i].Position + 1);

    yield return new CodeInstruction(OpCodes.Call,method);
    yield return new CodeInstruction(OpCodes.Ret);
}

由此调用

private void DoPatches() {
    Logger.Debug("Performing patches.");

    var methods = oldGuiType.getmethods((BindingFlags) FLAGS);
    
    var t = this.GetType().getmethod("transpiler",BindingFlags.NonPublic | BindingFlags.Static);

    for (var i = 0; i < methods.Length; i++) {
        var name = methods[i].Name;
        Logger.Debug($"Transpiling {name}");
        harmony.Patch(methods[i],transpiler: new HarmonyMethod(t));
    }
}

我不能使用前缀,因为我需要知道签名才能在前缀中获取参数,而我不知道签名。

我知道还有其他库基本上可以实现这一点,但是我正在修改的游戏附带 Harmony,所以我不必用我的非常小的 mod 运送整个库。

解决方法

对于原始方法的每次更改(例如添加或删除转译器)Harmony 将通过执行此操作(以伪代码)重新计算替换方法:

original_IL -> transpiler1 -> transpiler2 -> ... -> transpiler_n -> replacement_IL

其中转译器 1..n 都是活动转译器。然后它通过(大致)像这样构造替换来构建替换:

// Note: the following is very simplified and only used to illustrate
// the difference between prefix/postfix and a transpiler.

REPLACEMENT()
{
   if (Prefix_1() == false) return
   // ...
   if (Prefix_n() == false) return

   // replacement_IL_here

   Postfix_1()
   // ...
   Postfix_n()
}

因此,在内部,Harmony 必须跟踪所有补丁,并且由于这些补丁可能来自不同的程序集,因此需要序列化/反序列化状态 - 如果您有任意状态,这原则上是不可能的。因此,它只为每个可能的补丁存储最简单的键:它的 MethodInfo 必须是一个静态方法。

因此,您不能多次应用同一个补丁。这毫无意义,因为无论如何您都可以轻松地将所有代码放入一个补丁中。

,

对于此应用程序,即将方法更新为具有相同签名的更新方法,Memory.DetourMethod(MethodBase original,MethodBase replacement) 完成了我需要的操作。

// assuming newType is just a newer version of oldType
var newMethod = newType.getMethod("SomeMethod");
var oldMethod = oldType.getMethod("SomeMethod");
Memory.DetourMethod(oldMethod,newMethod);

这段代码有效地用新方法替换了旧方法。它的实际工作方式是 a bit more complicated

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。