如何解决如何使用 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 举报,一经查实,本站将立刻删除。