C# 合并两个方法的IL时出现dnlib.DotNet.Writer.ModuleWriterException

C# 合并两个方法的IL时出现dnlib.DotNet.Writer.ModuleWriterException,c#,dll,cil,dnlib,C#,Dll,Cil,Dnlib,我正在尝试合并两个方法的IL:一个方法来自已经存在的程序集(MyAssembly.dll),另一个与我的dnlib代码位于同一个项目中。我尝试读取这两个方法,合并它们的指令,然后将其写入新的程序集。但是,当我将if语句(可能还有其他语句)添加到新方法(因此是我的项目中的一个)时,我在将指令写入新程序集时遇到了一个异常: Unhandled Exception: dnlib.DotNet.Writer.ModuleWriterException: Found some other method's

我正在尝试合并两个方法的IL:一个方法来自已经存在的程序集(
MyAssembly.dll
),另一个与我的dnlib代码位于同一个项目中。我尝试读取这两个方法,合并它们的指令,然后将其写入新的程序集。但是,当我将if语句(可能还有其他语句)添加到新方法(因此是我的项目中的一个)时,我在将指令写入新程序集时遇到了一个异常:

Unhandled Exception: dnlib.DotNet.Writer.ModuleWriterException: Found some other method's instruction or a removed instruction. You probably removed an instruction that is the target of a branch instruction or an instruction that's the first/last inst
ruction in an exception handler.
   at dnlib.DotNet.DummyLogger.Log(Object sender, LoggerEvent loggerEvent, String format, Object[] args)
   at dnlib.DotNet.Writer.ModuleWriterBase.dnlib.DotNet.ILogger.Log(Object sender, LoggerEvent loggerEvent, String format, Object[] args)
   at dnlib.DotNet.Writer.MetaData.Error(String message, Object[] args)
   at dnlib.DotNet.Writer.MetaData.dnlib.DotNet.Writer.IWriterError.Error(String message)
   at dnlib.DotNet.Writer.MethodBodyWriter.ErrorImpl(String message)
   at dnlib.DotNet.Writer.MethodBodyWriterBase.Error(String message)
   at dnlib.DotNet.Writer.MethodBodyWriterBase.GetOffset(Instruction instr)
   at dnlib.DotNet.Writer.MethodBodyWriterBase.WriteShortInlineBrTarget(BinaryWriter writer, Instruction instr)
   at dnlib.DotNet.Writer.MethodBodyWriterBase.WriteOperand(BinaryWriter writer, Instruction instr)
   at dnlib.DotNet.Writer.MethodBodyWriterBase.WriteInstruction(BinaryWriter writer, Instruction instr)
   at dnlib.DotNet.Writer.MethodBodyWriterBase.WriteInstructions(BinaryWriter writer)
   at dnlib.DotNet.Writer.MethodBodyWriter.WriteFatHeader()
   at dnlib.DotNet.Writer.MethodBodyWriter.Write()
   at dnlib.DotNet.Writer.MetaData.WriteMethodBodies()
   at dnlib.DotNet.Writer.MetaData.Create()
   at dnlib.DotNet.Writer.MetaData.CreateTables()
   at dnlib.DotNet.Writer.ModuleWriter.WriteImpl()
   at dnlib.DotNet.Writer.ModuleWriterBase.Write(Stream dest)
   at dnlib.DotNet.Writer.ModuleWriterBase.Write(String fileName)
   at dnlib.DotNet.ModuleDef.Write(String filename, ModuleWriterOptions options)
   at dnlib.DotNet.ModuleDef.Write(String filename)
当原始代码(MyAssembly.dll中的代码)包含if语句时,它不会引发异常

MyAssembly.dll的代码:

string str = GameManager.m_GameVersionString;
if (GameManager.m_Changelist != string.Empty)
{
    str = str + " (" + GameManager.m_Changelist + ")";
}
return str + " Release ";
MyAssembly.dll的IL:

IL_0000: ldsfld System.String GameManager::m_GameVersionString
IL_0005: stloc.0
IL_0006: ldsfld System.String GameManager::m_Changelist
IL_000B: ldsfld System.String System.String::Empty
IL_0010: call System.Boolean System.String::op_Inequality(System.String,System.String)
IL_0015: brfalse IL_0030
IL_001A: ldloc.0
IL_001B: ldstr " ("
IL_0020: ldsfld System.String GameManager::m_Changelist
IL_0025: ldstr ")"
IL_002A: call System.String System.String::Concat(System.String,System.String,System.String,System.String)
IL_002F: stloc.0
IL_0030: ldloc.0
IL_0031: ldstr " Release "
IL_0036: call System.String System.String::Concat(System.String,System.String)
IL_003B: stloc.0
IL_003C: ldloc.0
IL_003D: ret
我的项目代码:

string check = "Hello".Trim();

if (check == "Hello")
{
    Console.WriteLine("Hello");
}
我的项目是IL

IL_0000: ldstr "Hello"
IL_0005: call System.String System.String::Trim()
IL_000A: ldstr "Hello"
IL_000F: call System.Boolean System.String::op_Equality(System.String,System.String)
IL_0014: brfalse.s IL_0020
IL_0016: ldstr "Hello"
IL_001B: call System.Void System.Console::WriteLine(System.String)
IL_0020: ret
合并IL

IL_0000: ldstr "Hello"
IL_0005: call System.String System.String::Trim()
IL_000A: ldstr "Hello"
IL_000F: call System.Boolean System.String::op_Equality(System.String,System.String)
IL_0014: brfalse.s IL_0020
IL_0016: ldstr "Hello"
IL_001B: call System.Void System.Console::WriteLine(System.String)
IL_0020: ldsfld System.String GameManager::m_GameVersionString
IL_0025: stloc.0
IL_0026: ldsfld System.String GameManager::m_Changelist
IL_002B: ldsfld System.String System.String::Empty
IL_0030: call System.Boolean System.String::op_Inequality(System.String,System.String)
IL_0035: brfalse.s IL_004D
IL_0037: ldloc.0
IL_0038: ldstr " ("
IL_003D: ldsfld System.String GameManager::m_Changelist
IL_0042: ldstr ")"
IL_0047: call System.String System.String::Concat(System.String,System.String,System.String,System.String)
IL_004C: stloc.0
IL_004D: ldloc.0
IL_004E: ldstr " Release "
IL_0053: call System.String System.String::Concat(System.String,System.String)
IL_0058: stloc.0
IL_0059: ldloc.0
IL_005A: ret
合并的IL看起来与使用dnSpy生成的IL相同:

但是,将合并的IL写入文件时会引发错误。我的代码:

//Patch method

...

var patchModule = ModuleDefMD.Load(patchClass.Module);
var patchMethod = FindMethod(patchModule, patchClass, method.Name);

var assemblyModule = ModuleDefMD.Load(assemblyPath);
var assemblyMethod = FindMethod(assemblyModule, attribute.Type, attribute.MethodName,  attribute.Parameters);

assemblyMethod.Body = MergeMethods(patchMethod, assemblyMethod, attribute.CodeMode, attribute.CustomPos);

assemblyModule.Write(finalPath); //Error is thrown here

...

private static CilBody MergeMethods(MethodDef original, MethodDef target, AmityPatch.Mode mode, int mergeLoc = 0)
{

    original.FreeMethodBody();
    target.FreeMethodBody();

    var originalBody = original.Body;
    var targetBody = target.Body;

    var targetModule = target.Module;

    var originalInstructions = originalBody.Instructions;
    var targetInstructions = targetBody.Instructions;

    Console.WriteLine("=original method=");
    Console.WriteLine();
    foreach (var originalInstruction in originalInstructions)
    {
        Console.WriteLine(originalInstruction);
    }
    Console.WriteLine();
    Console.WriteLine("=target method=");
    Console.WriteLine();
    foreach (var targetInstruction in targetInstructions)
    {
        Console.WriteLine(targetInstruction);
    }

    RemoveReturn(ref originalInstructions, true);
    if (mode == AmityPatch.Mode.Postfix) mergeLoc = targetInstructions.Count - 1;

    var localOffset = targetBody.Variables.Count;

    for (var i = 0; i < originalBody.Variables.Count; i++)
    {
        targetBody.Variables.Add(
            new Local(originalBody.Variables[i].Type, originalBody.Variables[i].Name));
    }

    for (var i = originalInstructions.Count - 1; i >= 0; i--)
    {
        var o = originalInstructions[i];
        var c = new Instruction(o.OpCode, o.Operand);

        switch (o.Operand)
        {
            case IType type:
                c.Operand = targetModule.Import(type);
                break;
            case IMethod method:
                c.Operand = targetModule.Import(method);
                break;
            case IField field:
                c.Operand = targetModule.Import(field);
                break;
        }

        if (IsStloc(o.OpCode))
        {
            c.OpCode = OpCodes.Stloc;
            c.Operand = targetBody.Variables[StlocIndex(o) + localOffset];
        }
        else if (IsLdloc(o.OpCode))
        {
            c.OpCode = OpCodes.Ldloc;
            c.Operand = targetBody.Variables[LdlocIndex(o) + localOffset];
        }
        else if (IsLdloca(o.OpCode))
        {
            c.OpCode = OpCodes.Ldloca;
            c.Operand = targetBody.Variables[LdlocIndex(o) + localOffset];
        }

        targetInstructions.Insert(mergeLoc, c);
    }

    targetBody.OptimizeMacros();
    targetBody.OptimizeBranches();

    Console.WriteLine();
    Console.WriteLine("=merged method=");
    Console.WriteLine();

    foreach (var instruction in targetBody.Instructions)
    {
        Console.WriteLine(instruction);
    }

    Console.WriteLine(targetBody.Variables.Count);

    return targetBody;
}
//补丁方法
...
var patchModule=ModuleDefMD.Load(patchClass.Module);
var patchMethod=FindMethod(patchModule、patchClass、method.Name);
var assemblyModule=ModuleDefMD.Load(组装路径);
var assemblyMethod=FindMethod(assemblyModule,attribute.Type,attribute.MethodName,attribute.Parameters);
assemblyMethod.Body=MergeMethods(patchMethod、assemblyMethod、attribute.CodeMode、attribute.CustomPos);
assemblyModule.Write(finalPath)//这里抛出错误
...
私有静态CilBody合并方法(MethodDef原始、MethodDef目标、AmityPatch.Mode模式、int mergeLoc=0)
{
原始.FreeMethodBody();
target.FreeMethodBody();
var originalBody=原始.Body;
var targetBody=target.Body;
var targetModule=target.Module;
var originalInstructions=原始主体。说明;
var targetInstructions=targetBody.Instructions;
Console.WriteLine(“=original method=”);
Console.WriteLine();
foreach(originalInstruction中的var originalInstruction)
{
Console.WriteLine(原始指令);
}
Console.WriteLine();
Console.WriteLine(“=target method=”);
Console.WriteLine();
foreach(targetInstructions中的var targetInstruction)
{
Console.WriteLine(目标指令);
}
RemoveReturn(参考原始说明,正确);
if(mode==AmityPatch.mode.Postfix)mergeLoc=targetInstructions.Count-1;
var localOffset=targetBody.Variables.Count;
对于(var i=0;i=0;i--)
{
var o=原始指令[i];
var c=新指令(操作码,操作数);
开关(操作数)
{
案例类型:
c、 操作数=targetModule.Import(类型);
打破
案例分析法:
c、 操作数=targetModule.Import(方法);
打破
案例字段:
c、 操作数=targetModule.Import(字段);
打破
}
if(IsStloc(o.OpCode))
{
c、 OpCode=OpCode.Stloc;
c、 操作数=targetBody.Variables[StlocIndex(o)+localOffset];
}
else if(IsLdloc(o.OpCode))
{
c、 操作码=操作码.Ldloc;
c、 操作数=targetBody.Variables[LdlocIndex(o)+localOffset];
}
else if(IsLdloca(o.OpCode))
{
c、 操作码=操作码.Ldloca;
c、 操作数=targetBody.Variables[LdlocIndex(o)+localOffset];
}
目标说明插入(mergeLoc,c);
}
targetBody.OptimizeMacros();
targetBody.optimizeBranchs();
Console.WriteLine();
Console.WriteLine(“=merged method=”);
Console.WriteLine();
foreach(targetBody.Instructions中的var指令)
{
控制台写入线(指令);
}
Console.WriteLine(targetBody.Variables.Count);
返回目标体;
}

我不知道是什么导致了这个问题。IL中的所有标签似乎都是正确的。当移除IF语句DNLIB时,将IL写入程序集。

< P>如果要合并的方法来自不同的程序集,则可能需要创建/转换令牌。