3

I'm trying to get a Mono.Cecil TypeDefinition from a .NET type and not having any luck.

I'm using code like this:

var type = typeof(MarkdownMonster.AppConfiguration);

var a = AssemblyDefinition.ReadAssembly(type.Assembly.Location);
var tr = a.MainModule.Import(type);  // this seems to work
var td = tr.Resolve(); // fails

but it fails with an assembly resolution error:

GetConfigurationPropertiesTest [0:29.990] Failed: Mono.Cecil.AssemblyResolutionException : Failed to resolve assembly: 'MarkdownMonster, Version=1.18.11.0, Culture=neutral, PublicKeyToken=null' Mono.Cecil.AssemblyResolutionException : Failed to resolve assembly: 'MarkdownMonster, Version=1.18.11.0, Culture=neutral, PublicKeyToken=null' at Mono.Cecil.BaseAssemblyResolver.Resolve(AssemblyNameReference name, ReaderParameters parameters) at Mono.Cecil.DefaultAssemblyResolver.Resolve(AssemblyNameReference name) at Mono.Cecil.MetadataResolver.Resolve(TypeReference type) at Mono.Cecil.TypeReference.Resolve() at Westwind.TypeImporter.TypeParser.ParseObject(Type type, Boolean dontParseMembers)

The assembly is obviously there, since the TypeReference import seems to work and produces a valid TypeReference.

The assembly in question is an EXE, and just for kicks I renamed it to a DLL but that had no effect.

Rick Strahl
  • 17,302
  • 14
  • 89
  • 134

2 Answers2

1

After a bit of back and forth experimenting I found one (ugly) solution is to create a custom type resolver and basically forcing a hard type reference into it. It seems Mono.Cecil is able to resolve transient dependencies once it's found the main assembly, but not the top level reference.

To make this work I basically pass in the already resolved assembly reference. In my case I know the only reference I will need to return will be the top level reference so I hard code this. A more realistic example will have to use AssemblyDefinition.ReadAssembly() to read an assembly off disk or from a stream.

Here's is the code to create the AssemblyResolver:

    public class MonoAssemblyResolver : IAssemblyResolver
    {
        public AssemblyDefinition AssemblyDefinition;

        public AssemblyDefinition Resolve(AssemblyNameReference name)
        {
            return AssemblyDefinition;
        }

        public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
        {

            return AssemblyDefinition;
        }

        public void Dispose()
        {
            AssemblyDefinition = null;
        }
    }

Note the cheat to pass in an already resolved assembly reference.

To resolve I can now use the following code:

var a = AssemblyDefinition.ReadAssembly(type.Assembly.Location,
            new ReaderParameters() { AssemblyResolver = resolver });

// assign the resolvedr
var resolver = new MonoAssemblyResolver();
resolver.AssemblyDefinition = a;

var tr = a.MainModule.Import(type: type);
var td = tr.Resolve();   // works now

This is crazy hacky, but can be adapted to be more generic (in my case not needed).

Still it would be much nicer if Mono.Cecil could automatically resolve the assembly - I don't understand why it's not finding the assembly in the first place since it lives in the current bin folder and the TypeReference can find it.

Rick Strahl
  • 17,302
  • 14
  • 89
  • 134
1

This is how it's normally done:

        var assembly = @"c:\myassembly.dll";

        var resolver = new DefaultAssemblyResolver();
        
        // add .NET runtime dir for the sake of security
        foreach (var dir in Directory.GetDirectories(RuntimeEnvironment.GetRuntimeDirectory(), "*", SearchOption.AllDirectories))
        {
            resolver.AddSearchDirectory(dir);
        }
        
        // add the assembly's directory
        resolver.AddSearchDirectory(Path.GetDirectoryName(assembly));

        var mod = AssemblyDefinition.ReadAssembly(assembly, new ReaderParameters { AssemblyResolver = resolver }).MainModule;

Regards

Martin.Martinsson
  • 1,894
  • 21
  • 25