2

If I was writing code this would be very simple. I have code that looks like:

public class SomeClass
{
    public void SomeMethod(ISomeService someService)
    {
    }

    private void AnotherMethod(ISomeService someService)
    {
    }
}

I can get the Method definition for both methods with the Type reference but what I'm trying to get is a call from SomeMethod to AnotherMethod added in IL so you would have the equivalent code like:

public void SomeMethod(ISomeService someService)
{
    AnotherMethod(someService);
}

I'm totally lost at the moment trying to understand how to properly construct the necessary instructions.

What I have right now looks something like:

private void ProcessType(TypeDefinition typeDef)
{
    var anotherMethodDef = typeDef.Methods.FirstOrDefault(x => HasMethod(x, "AnotherMethod"));
    if(someMethodDef != null)
    {
        var someMethodDef = typeDef.Methods.First(x => HasMethod(x, "SomeMethod"));
        var processor = someMethodDef.Body.GetILProcessor();

        // Now I need to generate:
        // AnotherMethod(someService); as a new instruction
    }
}

private static bool HasMethod(MethodDefinition method, string expected) =>
    method.Name == expected && method.Parameters.Count() == 1 &&
        method.Parameters.First().TypeDefinition.FullName == "Contoso.ISomeService";
Dan Siegel
  • 5,724
  • 2
  • 14
  • 28
  • I always start by writing as much as you can in C# and then disassembling it in e.g. ILSpy with the "IL" option instead of "C#". – Joe Sewell May 14 '20 at 19:56
  • yep did that... still not clear on exactly what I'm missing. The closest I could get results in a decompiler exception after tried inserting the new instruction. This seems like something that should be very easy to do, but I'm at a total loss of what I'm missing here. – Dan Siegel May 14 '20 at 20:11

1 Answers1

1

Not sure if this helps, but IL of SomeMethod:

    .maxstack 8

    IL_0000: nop
    IL_0001: ret

And IL of SomeMethod with AnotherMethod(someService);

    .maxstack 8

    IL_0000: nop
    IL_0001: ldarg.0
    IL_0002: ldarg.1
    IL_0003: call instance void SomeClass::AnotherMethod(class [YourAssembly]YourNamespace.ISomeService)
    IL_0008: nop
    IL_0009: ret

Sooo you want to add this in the middle:

    // Add breakpoint space (https://stackoverflow.com/questions/4590382/why-does-generated-il-code-start-with-a-nop)
    IL_0000: nop
    // Load `this` (https://stackoverflow.com/questions/1785372/why-do-i-have-to-do-ldarg-0-before-calling-a-field-in-msil)
    IL_0001: ldarg.0
    // Load your first argument... which is `someService`
    IL_0002: ldarg.1
    // Call method with this signature and previously loaded arguments
    IL_0003: call instance void SomeClass::AnotherMethod(class [YourAssembly]YourNamespace.ISomeService)

Finally this should do the trick:

var myAssembly = Assembly.GetAssembly(typeof(SomeClass));
var module = myAssembly.MainModule;
var anotherMethodRef = module.Import(typeof(SomeClass).GetMethod("AnotherMethod", new[] { typeof(ISomeService) }));

var nop = processor.Create(OpCodes.Nop);
var loadThis = processor.Create(OpCodes.Ldarg_0);
var loadSomeService = processor.Create(OpCodes.Ldarg_1);
var callMethod = processor.Create(OpCodes.Call, anotherMethodRef);

// First instruction was IL_0000: nop
var firstInstruction = someMethodDef.Body.Instructions[0];

processor.InsertBefore(firstInstruction, nop);
processor.InsertBefore(firstInstruction, loadThis);
processor.InsertBefore(firstInstruction, loadSomeService);
processor.InsertBefore(firstInstruction, callMethod);
Šimon Kocúrek
  • 1,591
  • 1
  • 12
  • 22
  • 1
    beautiful... thanks for the walk through of the IL and Mono.Cecil code.. this is a totally new way to look at the code so I'm still trying to wrap my head around it! – Dan Siegel May 14 '20 at 20:59