views:

39

answers:

1

I'm using Mono.Cecil (version 0.6.9.0) to alias a method that implements an interface method. To do that, I have to add an Override to the target method that points back to the interface method (much like what's possible with VB.NET), like this:

using System;
using System.Reflection;
using Mono.Cecil;

class Program {

  static void Main(string[] args) {
    var asm = AssemblyFactory.GetAssembly(Assembly.GetExecutingAssembly().Location);

    var source = asm.MainModule.Types["A"];
    var sourceMethod = source.Methods[0];
    var sourceRef = new MethodReference(
      sourceMethod.Name,
      sourceMethod.DeclaringType,
      sourceMethod.ReturnType.ReturnType,
      sourceMethod.HasThis,
      sourceMethod.ExplicitThis,
      sourceMethod.CallingConvention);

    var target = asm.MainModule.Types["C"];
    var targetMethod = target.Methods[0];
    targetMethod.Name = "AliasedMethod";
    targetMethod.Overrides.Add(sourceRef);

    AssemblyAssert.Verify(asm); // this will just run PEVerify on the changed assembly
  }

}

interface A {
  void Method();
}

class C : A {
  public void Method() { }
}

What I'm getting is a PEVerify.exe error that indicates that my class doesn't implement the interface method anymore. It seems that there's a token mismatch in the changed assembly between the override reference and the method in the interface:

[MD]: Error: Class implements interface but not method (class:0x02000004; interface:0x02000002; method:0x06000001). [token:0x09000001]

I know that if I use the MethodDefinition directly it will work:

targetMethod.Overrides.Add(sourceMethod);

But I really need to use a MethodReference because I might have generic parameters and arguments in the types involved, and a simple MethodDefinition won't do.

Update: As recommended by Jb Evain, I've migrated to version 0.9.3.0. But, the same error still happens. Here's the migrated code:

var module = ModuleDefinition.ReadModule(Assembly.GetExecutingAssembly().Location);

var source = module.GetType("A");
var sourceMethod = source.Methods[0];

var sourceRef = new MethodReference(
  sourceMethod.Name,
  sourceMethod.ReturnType) {
    DeclaringType = sourceMethod.DeclaringType,
    HasThis = sourceMethod.HasThis,
    ExplicitThis = sourceMethod.ExplicitThis,
    CallingConvention = sourceMethod.CallingConvention 
};

var target = module.GetType("C");
var targetMethod = target.Methods[0];
targetMethod.Name = "AliasedMethod";
targetMethod.Overrides.Add(sourceRef);
+2  A: 

That's one of the annoyance of using the 0.6. You have to put the newly created MethodReference in the .MemberReferences collection of your module.

I strongly suggest you switch to 0.9.

Jb Evain
Thanks a lot, but I've tried that as well, and it didn't work.
Jordão
I've just migrated it, and I get the same error. I'll update the question.
Jordão
Weird, I'll investigate.
Jb Evain
Jordão: it seems that it's a peverify bug to me. The method reference is properly emitted, but peverify doesn't like it.If I use your exact scenario, but with a method on a generic type, it works just fine.So it seems to me that you'll have to deal with it yourself, that is, putting the MethodDefinition if the method is not on a generic type and is defined in the current assembly, or you'll create a MethodReference.
Jb Evain
It works for me! Thanks a lot for the answer and for the amazing library!
Jordão
Do you know of any programmatic way of verifying an assembly (maybe in the Mono project), without using PEVerify?
Jordão
Cool, glad to know it works. As for verifying assemblies programmaticaly, there's no cross-platform way. All verifiers are usually very tied to the runtime. So if you're ok with it you can probably call unmanaged methods to do so. But I never went this far, and always resorted to call peverify on both .net and Mono.
Jb Evain