Search code examples
c#.netreflection.net-assemblysystem.reflection

Finding classes in a DLL that inherit from classes in an unavailable assembly


I'm developing a tool that loads add-ins into a piece of commercial software we use in my office. Add-ins for this software can be developed by creating a .NET assembly with classes that inherit from classes in the software's API.

So my software needs to inspect DLLs to determine if they contain classes which inherit from the API add-in classes by name. However, I don't want my application to depend on the API itself. It doesn't need to instantiate or execute any of these classes only determine the full names of classes which inherit from the API add-in classes.

I am able to load the dll assembly using:

Assembly.LoadFrom(dllFileName)

Yet when I try to call GetTypes() on the loaded assembly. I get a ReflectionTypeLoadException with a number of LoaderExceptions complaining that the API files can not be found.

I also tried using ReflectionOnlyLoadFrom() but it threw the same exception.

Is there some other way to get this information? Again I don't need to execute any code, and ultimately I'd like my tool to work on a computer that doesn't have the API or software installed at all. All I need to know is the full classes names.


Solution

  • This cannot be done using the framework's Reflection API. Reflection requires access to the assemblies to load the type metadata. What you could do is use something like Mono.Cecil (available on nuget) to inspect the MSIL. Here is a sample showing types with base classes and interfaces in an assembly:

    using Mono.Cecil;
    using Mono.Cecil.Cil;
    
    var assembly = AssemblyDefinition.ReadAssembly("ClassLibrary.dll");
    // skip the first type '<Module>' whatever that is.
    var types = assembly.MainModule.Types.Skip(1);
    
    foreach (var type in types)
    {
        var interfaces = type.Interfaces.Select(i => i.FullName);
        if (interfaces.Any())
        {
             Console.WriteLine("{0} : {1}, {2}", type.FullName, type.BaseType.FullName, string.Join(", ", interfaces));
        }
        else
        {
             Console.WriteLine("{0} : {1}", type.FullName, type.BaseType.FullName);
        }
    }
    

    This does not cause the assembly or any of its dependencies to be loaded. This would be useful when you know the classes you are interested in are directly inherited from the foreign API classes. If there are several levels of inheritance then you will have to do more work than I've shown above.