C# CIL stloc.1 问题
C# CIL stloc.1 issue
前一题已解决,请看最后
所以我这里有这段代码:
using Harmony;
using RimWorld;
using Verse;
using UnityEngine;
using System.Collections.Generic;
using System.Reflection.Emit;
using System;
using System.Reflection;
namespace RandomPlus
{
[HarmonyPatch (typeof(Page_ConfigureStartingPawns), "RandomizeCurPawn")]
class Patch_RandomizeMethod
{
static void Prefix ()
{
RandomSettings.ResetRerollCounter ();
}
static IEnumerable<CodeInstruction> Transpiler (IEnumerable<CodeInstruction> instructions)
{
var curPawnFieldInfo = typeof(Page_ConfigureStartingPawns)
.GetField ("curPawn", BindingFlags.NonPublic | BindingFlags.Instance);
var randomizeInPlaceMethodInfo = typeof(StartingPawnUtility)
.GetMethod ("RandomizeInPlace", BindingFlags.Public | BindingFlags.Static);
var checkPawnIsSatisfiedMethodInfo = typeof(RandomSettings)
.GetMethod ("CheckPawnIsSatisfied", BindingFlags.Public | BindingFlags.Static);
var codes = new List<CodeInstruction> (instructions);
var appropriatePlace = 6;
/* Removing the following code in its IL form */
// this.curPawn = StartingPawnUtility.RandomizeInPlace(this.curPawn);
codes.RemoveRange (appropriatePlace, 5);
/* Adding the following code in its IL form: */
// do {
// this.curPawn = StartingPawnUtility.RandomizeInPlace (this.curPawn);
// } while (!RandomSettings.CheckPawnIsSatisfiedMethodInfo);
//
// // loop start (head: IL_0016)
// IL_0016: nop
// IL_0017: ldarg.0
// IL_0018: ldarg.0
// IL_0019: ldarg.0
// IL_001a: ldfld int32 C::curPawn
// IL_001f: call instance int32 C::RandomizeInPlace(int32)
// IL_0024: stfld int32 C::curPawn
// IL_0029: nop
// IL_002a: ldarg.0
// IL_002b: call instance bool C::CheckPawnIsSatisfied()
// IL_0030: ldc.i4.0
// IL_0031: ceq
// IL_0033: stloc.1
// // sequence point: hidden
// IL_0034: ldloc.1
// IL_0035: brtrue.s IL_0016
// // end loop
List <CodeInstruction> newCodes = new List<CodeInstruction> {
new CodeInstruction (OpCodes.Nop),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Call, randomizeInPlaceMethodInfo),
new CodeInstruction (OpCodes.Stfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Nop),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Call, checkPawnIsSatisfiedMethodInfo),
new CodeInstruction (OpCodes.Ldc_I4_0),
new CodeInstruction (OpCodes.Ceq),
new CodeInstruction (OpCodes.Stloc_1),
new CodeInstruction (OpCodes.Ldloc_1),
};
newCodes [0].labels.Add (new Label ());
var nopLabel = newCodes [0].labels [0];
newCodes.Add (new CodeInstruction (OpCodes.Brtrue_S, nopLabel));
codes.InsertRange (appropriatePlace, newCodes);
for (var i = 0; i < codes.Count; i++) {
Log.Message (codes [i].ToString ());
}
return codes;
}
}
}
它基本上做的是改变 IL 中的方法代码,它应该改变这个
private void RandomizeCurPawn()
{
if (!TutorSystem.AllowAction("RandomizePawn"))
{
return;
}
this.curPawn = StartingPawnUtility.RandomizeInPlace(this.curPawn);
TutorSystem.Notify_Event("RandomizePawn");
}
进入:
private void RandomizeCurPawn()
{
if (!TutorSystem.AllowAction("RandomizePawn"))
{
return;
}
do
{
this.curPawn = StartingPawnUtility.RandomizeInPlace(this.curPawn);
}
while (!RandomSettings.CheckPawnIsSatisfiedMethodInfo);
TutorSystem.Notify_Event("RandomizePawn");
}
为了获得 "while" 部分的 IL 代码,我想出了 this example program 这样我就可以获得代码并根据需要进行更改,但尽管我复制了 IL代码正确(据我所知),它不起作用并抛出一个异常:
Could not execute post-long-event action. Exception: System.TypeInitializationException: An exception was thrown by the type initializer for RandomPlus.HarmonyPatches ---> System.FormatException: Method RimWorld.Page_ConfigureStartingPawns.RandomizeCurPawn() cannot be patched. Reason: Invalid IL code in (wrapper dynamic-method) RimWorld.Page_ConfigureStartingPawns:RandomizeCurPawn_Patch1 (object): IL_003c: stloc.1
at Harmony.PatchFunctions.UpdateWrapper (System.Reflection.MethodBase original, Harmony.PatchInfo patchInfo, System.String instanceID) [0x00000] in <filename unknown>:0
at Harmony.PatchProcessor.Patch () [0x00000] in <filename unknown>:0
at Harmony.HarmonyInstance.<PatchAll>b__7_0 (System.Type type) [0x00000] in <filename unknown>:0
at Harmony.CollectionExtensions.Do[Type] (IEnumerable`1 sequence, System.Action`1 action) [0x00000] in <filename unknown>:0
at Harmony.HarmonyInstance.PatchAll (System.Reflection.Assembly assembly) [0x00000] in <filename unknown>:0
at RandomPlus.HarmonyPatches..cctor () [0x00000] in <filename unknown>:0
--- End of inner exception stack trace ---
at (wrapper managed-to-native) System.Runtime.CompilerServices.RuntimeHelpers:RunClassConstructor (intptr)
at System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (RuntimeTypeHandle type) [0x00000] in <filename unknown>:0
at Verse.StaticConstructorOnStartupUtility.CallAll () [0x00000] in <filename unknown>:0
at Verse.PlayDataLoader.<DoPlayLoad>m__2 () [0x00000] in <filename unknown>:0
at Verse.LongEventHandler.ExecuteToExecuteWhenFinished () [0x00000] in <filename unknown>:0
Verse.Log:Error(String)
Verse.LongEventHandler:ExecuteToExecuteWhenFinished()
Verse.LongEventHandler:UpdateCurrentAsynchronousEvent()
Verse.LongEventHandler:LongEventsUpdate(Boolean&)
Verse.Root:Update()
Verse.Root_Entry:Update()
如您所见,它在抱怨 "stloc.1" 行,我不确定为什么。如果有人知道如何解决这个问题,请告诉我,我将不胜感激!
UPD.
我想在这里问另一个问题,而不是创建一个单独的问题。
更改内容如下:
// // loop start (head: IL_000e)
// IL_000e: ldarg.0
// IL_000f: ldarg.0
// IL_0010: ldarg.0
// IL_0011: ldfld int32 C::curPawn
// IL_0016: call instance int32 C::RandomizeInPlace(int32)
// IL_001b: stfld int32 C::curPawn
// IL_0020: ldarg.0
// IL_0021: call instance bool C::CheckPawnIsSatisfied()
// IL_0026: brfalse.s IL_000e
// // end loop
List <CodeInstruction> newCodes = new List<CodeInstruction> {
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Call, randomizeInPlaceMethodInfo),
new CodeInstruction (OpCodes.Stfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Call, checkPawnIsSatisfiedMethodInfo),
};
游戏现在显示此错误:
Could not execute post-long-event action. Exception: System.TypeInitializationException: An exception was thrown by the type initializer for RandomPlus.HarmonyPatches ---> System.FormatException: Method RimWorld.Page_ConfigureStartingPawns.RandomizeCurPawn() cannot be patched. Reason: Invalid IL code in (wrapper dynamic-method) RimWorld.Page_ConfigureStartingPawns:RandomizeCurPawn_Patch1 (object): IL_003c: call 0x00000011
at Harmony.PatchFunctions.UpdateWrapper (System.Reflection.MethodBase original, Harmony.PatchInfo patchInfo, System.String instanceID) [0x00000] in <filename unknown>:0
at Harmony.PatchProcessor.Patch () [0x00000] in <filename unknown>:0
at Harmony.HarmonyInstance.<PatchAll>b__7_0 (System.Type type) [0x00000] in <filename unknown>:0
at Harmony.CollectionExtensions.Do[Type] (IEnumerable`1 sequence, System.Action`1 action) [0x00000] in <filename unknown>:0
at Harmony.HarmonyInstance.PatchAll (System.Reflection.Assembly assembly) [0x00000] in <filename unknown>:0
at RandomPlus.HarmonyPatches..cctor () [0x00000] in <filename unknown>:0
--- End of inner exception stack trace ---
at (wrapper managed-to-native) System.Runtime.CompilerServices.RuntimeHelpers:RunClassConstructor (intptr)
at System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (RuntimeTypeHandle type) [0x00000] in <filename unknown>:0
at Verse.StaticConstructorOnStartupUtility.CallAll () [0x00000] in <filename unknown>:0
at Verse.PlayDataLoader.<DoPlayLoad>m__2 () [0x00000] in <filename unknown>:0
at Verse.LongEventHandler.ExecuteToExecuteWhenFinished () [0x00000] in <filename unknown>:0
Verse.Log:Error(String)
Verse.LongEventHandler:ExecuteToExecuteWhenFinished()
你认为这是因为我没有为 RandomizeInPlace 传递参数吗?
UPD2:
我更新了 playground,这是我当前的代码和错误:
List <CodeInstruction> newCodes = new List<CodeInstruction> {
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Call, randomizeInPlaceMethodInfo),
new CodeInstruction (OpCodes.Stfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Call, checkPawnIsSatisfiedMethodInfo),
};
Could not execute post-long-event action. Exception: System.TypeInitializationException: An exception was thrown by the type initializer for RandomPlus.HarmonyPatches ---> System.FormatException: Method RimWorld.Page_ConfigureStartingPawns.RandomizeCurPawn() cannot be patched. Reason: Invalid IL code in (wrapper dynamic-method) RimWorld.Page_ConfigureStartingPawns:RandomizeCurPawn_Patch1 (object): IL_003a: call 0x00000011
据我所知:
- 在您的代码中,您没有局部变量。所以
stloc.1
不会起作用。
- 在您的示例中,您正在查看调试代码。这是创建和设置局部变量以简化调试※。请改用发布代码。
※:看到代码中使用了stloc.0
(设置第一个局部变量)后跟ldloc.0
(加载第一个局部变量),其他地方没有使用它们。 stloc.1
和ldloc.1
也是如此。 stloc
和 ldloc
之间有 "sequence point: hidden",表示可以添加断点并检查辅助局部变量值的位置。
如果你坚持要加局部变量,看看ILGenerator.DeclareLocal。
附录
在原始代码中,你有:
this.curPawn = StartingPawnUtility.RandomizeInPlace(this.curPawn);
这是在做三件事:
- 阅读
curPawn
(这里需要this
)
- 调用
StartingPawnUtility.RandomizeInPlace
(这里不需要this
,这是一个静态调用)
- 设置
curPawn
(这里需要this
)
总共使用了 2 次this
。因此,代码需要加载 this
(a.k.a ldarg.0
) 两次。
现在,替换代码有:
this.curPawn = this.RandomizeInPlace (this.curPawn);
这里,对RandomizeInPlace
的调用不是静态调用。因此,代码需要再次加载 this
,总共三次(您甚至在代码中显式编写了 this
的三种用法)。
这就是为什么你的代码中有三个连续的 ldarg.0
:
List <CodeInstruction> newCodes = new List<CodeInstruction> {
new CodeInstruction (OpCodes.Ldarg_0), // <--
new CodeInstruction (OpCodes.Ldarg_0), // <--
new CodeInstruction (OpCodes.Ldarg_0), // <--
new CodeInstruction (OpCodes.Ldfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Call, randomizeInPlaceMethodInfo),
new CodeInstruction (OpCodes.Stfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Call, checkPawnIsSatisfiedMethodInfo),
};
我想你想做一个静态调用,就像原来的代码一样,这意味着删除一个 ldarg.0
,只留下两个。
注意:我认为你与其他调用指令有类似的问题。
您可以查看 OpCodes 以获取有关 IL 指令的文档。
我已经解决了这个问题!
事实证明,checkPawnIsSatisfied 必须接收 this.curPawn 作为参数,所以我添加了一行将其作为参数传递给方法,它开始工作了!
List <CodeInstruction> newCodes = new List<CodeInstruction> {
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Call, randomizeInPlaceMethodInfo),
new CodeInstruction (OpCodes.Stfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Call, checkPawnIsSatisfiedMethodInfo),
};
前一题已解决,请看最后
所以我这里有这段代码:
using Harmony;
using RimWorld;
using Verse;
using UnityEngine;
using System.Collections.Generic;
using System.Reflection.Emit;
using System;
using System.Reflection;
namespace RandomPlus
{
[HarmonyPatch (typeof(Page_ConfigureStartingPawns), "RandomizeCurPawn")]
class Patch_RandomizeMethod
{
static void Prefix ()
{
RandomSettings.ResetRerollCounter ();
}
static IEnumerable<CodeInstruction> Transpiler (IEnumerable<CodeInstruction> instructions)
{
var curPawnFieldInfo = typeof(Page_ConfigureStartingPawns)
.GetField ("curPawn", BindingFlags.NonPublic | BindingFlags.Instance);
var randomizeInPlaceMethodInfo = typeof(StartingPawnUtility)
.GetMethod ("RandomizeInPlace", BindingFlags.Public | BindingFlags.Static);
var checkPawnIsSatisfiedMethodInfo = typeof(RandomSettings)
.GetMethod ("CheckPawnIsSatisfied", BindingFlags.Public | BindingFlags.Static);
var codes = new List<CodeInstruction> (instructions);
var appropriatePlace = 6;
/* Removing the following code in its IL form */
// this.curPawn = StartingPawnUtility.RandomizeInPlace(this.curPawn);
codes.RemoveRange (appropriatePlace, 5);
/* Adding the following code in its IL form: */
// do {
// this.curPawn = StartingPawnUtility.RandomizeInPlace (this.curPawn);
// } while (!RandomSettings.CheckPawnIsSatisfiedMethodInfo);
//
// // loop start (head: IL_0016)
// IL_0016: nop
// IL_0017: ldarg.0
// IL_0018: ldarg.0
// IL_0019: ldarg.0
// IL_001a: ldfld int32 C::curPawn
// IL_001f: call instance int32 C::RandomizeInPlace(int32)
// IL_0024: stfld int32 C::curPawn
// IL_0029: nop
// IL_002a: ldarg.0
// IL_002b: call instance bool C::CheckPawnIsSatisfied()
// IL_0030: ldc.i4.0
// IL_0031: ceq
// IL_0033: stloc.1
// // sequence point: hidden
// IL_0034: ldloc.1
// IL_0035: brtrue.s IL_0016
// // end loop
List <CodeInstruction> newCodes = new List<CodeInstruction> {
new CodeInstruction (OpCodes.Nop),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Call, randomizeInPlaceMethodInfo),
new CodeInstruction (OpCodes.Stfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Nop),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Call, checkPawnIsSatisfiedMethodInfo),
new CodeInstruction (OpCodes.Ldc_I4_0),
new CodeInstruction (OpCodes.Ceq),
new CodeInstruction (OpCodes.Stloc_1),
new CodeInstruction (OpCodes.Ldloc_1),
};
newCodes [0].labels.Add (new Label ());
var nopLabel = newCodes [0].labels [0];
newCodes.Add (new CodeInstruction (OpCodes.Brtrue_S, nopLabel));
codes.InsertRange (appropriatePlace, newCodes);
for (var i = 0; i < codes.Count; i++) {
Log.Message (codes [i].ToString ());
}
return codes;
}
}
}
它基本上做的是改变 IL 中的方法代码,它应该改变这个
private void RandomizeCurPawn()
{
if (!TutorSystem.AllowAction("RandomizePawn"))
{
return;
}
this.curPawn = StartingPawnUtility.RandomizeInPlace(this.curPawn);
TutorSystem.Notify_Event("RandomizePawn");
}
进入:
private void RandomizeCurPawn()
{
if (!TutorSystem.AllowAction("RandomizePawn"))
{
return;
}
do
{
this.curPawn = StartingPawnUtility.RandomizeInPlace(this.curPawn);
}
while (!RandomSettings.CheckPawnIsSatisfiedMethodInfo);
TutorSystem.Notify_Event("RandomizePawn");
}
为了获得 "while" 部分的 IL 代码,我想出了 this example program 这样我就可以获得代码并根据需要进行更改,但尽管我复制了 IL代码正确(据我所知),它不起作用并抛出一个异常:
Could not execute post-long-event action. Exception: System.TypeInitializationException: An exception was thrown by the type initializer for RandomPlus.HarmonyPatches ---> System.FormatException: Method RimWorld.Page_ConfigureStartingPawns.RandomizeCurPawn() cannot be patched. Reason: Invalid IL code in (wrapper dynamic-method) RimWorld.Page_ConfigureStartingPawns:RandomizeCurPawn_Patch1 (object): IL_003c: stloc.1
at Harmony.PatchFunctions.UpdateWrapper (System.Reflection.MethodBase original, Harmony.PatchInfo patchInfo, System.String instanceID) [0x00000] in <filename unknown>:0
at Harmony.PatchProcessor.Patch () [0x00000] in <filename unknown>:0
at Harmony.HarmonyInstance.<PatchAll>b__7_0 (System.Type type) [0x00000] in <filename unknown>:0
at Harmony.CollectionExtensions.Do[Type] (IEnumerable`1 sequence, System.Action`1 action) [0x00000] in <filename unknown>:0
at Harmony.HarmonyInstance.PatchAll (System.Reflection.Assembly assembly) [0x00000] in <filename unknown>:0
at RandomPlus.HarmonyPatches..cctor () [0x00000] in <filename unknown>:0
--- End of inner exception stack trace ---
at (wrapper managed-to-native) System.Runtime.CompilerServices.RuntimeHelpers:RunClassConstructor (intptr)
at System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (RuntimeTypeHandle type) [0x00000] in <filename unknown>:0
at Verse.StaticConstructorOnStartupUtility.CallAll () [0x00000] in <filename unknown>:0
at Verse.PlayDataLoader.<DoPlayLoad>m__2 () [0x00000] in <filename unknown>:0
at Verse.LongEventHandler.ExecuteToExecuteWhenFinished () [0x00000] in <filename unknown>:0
Verse.Log:Error(String)
Verse.LongEventHandler:ExecuteToExecuteWhenFinished()
Verse.LongEventHandler:UpdateCurrentAsynchronousEvent()
Verse.LongEventHandler:LongEventsUpdate(Boolean&)
Verse.Root:Update()
Verse.Root_Entry:Update()
如您所见,它在抱怨 "stloc.1" 行,我不确定为什么。如果有人知道如何解决这个问题,请告诉我,我将不胜感激!
UPD.
我想在这里问另一个问题,而不是创建一个单独的问题。
更改内容如下:
// // loop start (head: IL_000e)
// IL_000e: ldarg.0
// IL_000f: ldarg.0
// IL_0010: ldarg.0
// IL_0011: ldfld int32 C::curPawn
// IL_0016: call instance int32 C::RandomizeInPlace(int32)
// IL_001b: stfld int32 C::curPawn
// IL_0020: ldarg.0
// IL_0021: call instance bool C::CheckPawnIsSatisfied()
// IL_0026: brfalse.s IL_000e
// // end loop
List <CodeInstruction> newCodes = new List<CodeInstruction> {
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Call, randomizeInPlaceMethodInfo),
new CodeInstruction (OpCodes.Stfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Call, checkPawnIsSatisfiedMethodInfo),
};
游戏现在显示此错误:
Could not execute post-long-event action. Exception: System.TypeInitializationException: An exception was thrown by the type initializer for RandomPlus.HarmonyPatches ---> System.FormatException: Method RimWorld.Page_ConfigureStartingPawns.RandomizeCurPawn() cannot be patched. Reason: Invalid IL code in (wrapper dynamic-method) RimWorld.Page_ConfigureStartingPawns:RandomizeCurPawn_Patch1 (object): IL_003c: call 0x00000011
at Harmony.PatchFunctions.UpdateWrapper (System.Reflection.MethodBase original, Harmony.PatchInfo patchInfo, System.String instanceID) [0x00000] in <filename unknown>:0
at Harmony.PatchProcessor.Patch () [0x00000] in <filename unknown>:0
at Harmony.HarmonyInstance.<PatchAll>b__7_0 (System.Type type) [0x00000] in <filename unknown>:0
at Harmony.CollectionExtensions.Do[Type] (IEnumerable`1 sequence, System.Action`1 action) [0x00000] in <filename unknown>:0
at Harmony.HarmonyInstance.PatchAll (System.Reflection.Assembly assembly) [0x00000] in <filename unknown>:0
at RandomPlus.HarmonyPatches..cctor () [0x00000] in <filename unknown>:0
--- End of inner exception stack trace ---
at (wrapper managed-to-native) System.Runtime.CompilerServices.RuntimeHelpers:RunClassConstructor (intptr)
at System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (RuntimeTypeHandle type) [0x00000] in <filename unknown>:0
at Verse.StaticConstructorOnStartupUtility.CallAll () [0x00000] in <filename unknown>:0
at Verse.PlayDataLoader.<DoPlayLoad>m__2 () [0x00000] in <filename unknown>:0
at Verse.LongEventHandler.ExecuteToExecuteWhenFinished () [0x00000] in <filename unknown>:0
Verse.Log:Error(String)
Verse.LongEventHandler:ExecuteToExecuteWhenFinished()
你认为这是因为我没有为 RandomizeInPlace 传递参数吗?
UPD2:
我更新了 playground,这是我当前的代码和错误:
List <CodeInstruction> newCodes = new List<CodeInstruction> {
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Call, randomizeInPlaceMethodInfo),
new CodeInstruction (OpCodes.Stfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Call, checkPawnIsSatisfiedMethodInfo),
};
Could not execute post-long-event action. Exception: System.TypeInitializationException: An exception was thrown by the type initializer for RandomPlus.HarmonyPatches ---> System.FormatException: Method RimWorld.Page_ConfigureStartingPawns.RandomizeCurPawn() cannot be patched. Reason: Invalid IL code in (wrapper dynamic-method) RimWorld.Page_ConfigureStartingPawns:RandomizeCurPawn_Patch1 (object): IL_003a: call 0x00000011
据我所知:
- 在您的代码中,您没有局部变量。所以
stloc.1
不会起作用。 - 在您的示例中,您正在查看调试代码。这是创建和设置局部变量以简化调试※。请改用发布代码。
※:看到代码中使用了stloc.0
(设置第一个局部变量)后跟ldloc.0
(加载第一个局部变量),其他地方没有使用它们。 stloc.1
和ldloc.1
也是如此。 stloc
和 ldloc
之间有 "sequence point: hidden",表示可以添加断点并检查辅助局部变量值的位置。
如果你坚持要加局部变量,看看ILGenerator.DeclareLocal。
附录
在原始代码中,你有:
this.curPawn = StartingPawnUtility.RandomizeInPlace(this.curPawn);
这是在做三件事:
- 阅读
curPawn
(这里需要this
) - 调用
StartingPawnUtility.RandomizeInPlace
(这里不需要this
,这是一个静态调用) - 设置
curPawn
(这里需要this
)
总共使用了 2 次this
。因此,代码需要加载 this
(a.k.a ldarg.0
) 两次。
现在,替换代码有:
this.curPawn = this.RandomizeInPlace (this.curPawn);
这里,对RandomizeInPlace
的调用不是静态调用。因此,代码需要再次加载 this
,总共三次(您甚至在代码中显式编写了 this
的三种用法)。
这就是为什么你的代码中有三个连续的 ldarg.0
:
List <CodeInstruction> newCodes = new List<CodeInstruction> {
new CodeInstruction (OpCodes.Ldarg_0), // <--
new CodeInstruction (OpCodes.Ldarg_0), // <--
new CodeInstruction (OpCodes.Ldarg_0), // <--
new CodeInstruction (OpCodes.Ldfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Call, randomizeInPlaceMethodInfo),
new CodeInstruction (OpCodes.Stfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Call, checkPawnIsSatisfiedMethodInfo),
};
我想你想做一个静态调用,就像原来的代码一样,这意味着删除一个 ldarg.0
,只留下两个。
注意:我认为你与其他调用指令有类似的问题。
您可以查看 OpCodes 以获取有关 IL 指令的文档。
我已经解决了这个问题!
事实证明,checkPawnIsSatisfied 必须接收 this.curPawn 作为参数,所以我添加了一行将其作为参数传递给方法,它开始工作了!
List <CodeInstruction> newCodes = new List<CodeInstruction> {
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Call, randomizeInPlaceMethodInfo),
new CodeInstruction (OpCodes.Stfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Call, checkPawnIsSatisfiedMethodInfo),
};