简体   繁体   中英

Loading External Assembly using reflection

I'm trying to develop an application that supports dynamic loading of external modules. I have read several articles from loading external assemblies using C# (.NET v4.5) and got the code below. However, it is not working, not detecting my subclass on the external module.

Here is the code for loading external assembly:

byte[] array = <HERE I LOAD THE DLL>
Assembly asb = Assembly.Load(array);
Type[] types = GetAssemblyTypes(asb);
for( int i = 0; i < types.Length; i++ )
{
    Type t = types[i];
    if( t != null && typeof(App).IsAssignableFrom(t) /*t.IsSubclassOf(typeof(App))*/ )
    {
        app.AppClass = (App)Activator.CreateInstance(t);
        return true;
    }
}

Here is the GetAssemblyTypes()

private Type[] GetAssemblyTypes(Assembly asb)
{
    Type[] types;
    try
    {
        types = asb.GetTypes();
    }
    catch( ReflectionTypeLoadException ex )
    {
        types = ex.Types;
    }

    return types;
}

Here is the class on the MAIN APPLICATION (This class will be used by the modules)

namespace MyApplication.API
{
    public class App
    {
       // CODE
    }
}

Here is the example of my module: using MyApplication.API;

namespace HelloWorld
{
    class HelloWorld : App
    {

    }
}

Important points are: 1 - I don't know the class name of the module, I just know that it will be a subclass of the App class.

The issue is that although the types.Length gives me 1, when I tries to access by types[i] it gives a null pointer. Am I missing something here?

I have done something similar to find out if the assembly is derived from my base assembly

    var assembly = System.Reflection.Assembly.LoadFrom(file);
                AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += CurrentDomain_ReflectionOnlyAssemblyResolve;
                var derivedAssemblies = assembly.GetExportedTypes().Where(w => w.IsSubclassOf(typeof(AddressManager.Base.Connector.ConnectorBase))).Count(); 
                AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= CurrentDomain_ReflectionOnlyAssemblyResolve;

                assembly = null;
                if (derivedAssemblies > 0)
                {
                    Manager.LoadAssembly(file, "Connectors");
                    Trace.TraceInformation(" Success! Library loaded.");
                }
                else
                    Trace.TraceInformation(" Skipped! Not a subclass of '" + typeof(AddressManager.Base.Connector.ConnectorBase).Name + "'.");

And handle the ReflectionOnlyAssemblyResolve Event:

   private System.Reflection.Assembly CurrentDomain_ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args)
    {
        var assembly = AppDomain.CurrentDomain.GetAssemblies().Where(w => w.FullName == args.Name).FirstOrDefault();
        return assembly;
    }

I got a semi working version based on @CadBurry code.

byte[] bytes = <HERE I LOAD THE DLL>
Assembly asb = Assembly.Load(bytes);

AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += CurrentDomain_ReflectionOnlyAssemblyResolve;
IEnumerable<Type> types = asb.GetExportedTypes().Where(w => w.IsSubclassOf(typeof(App)));
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= CurrentDomain_ReflectionOnlyAssemblyResolve;
AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve;

if(types.Count() > 0)
{
    Type type = types.FirstOrDefault();

    if( type == null )
        return false;

    app.AppClass = (App)Activator.CreateInstance(type);
    return true;
}

with the methods:

private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    if( args.Name.Contains(typeof(MyApplication).Assembly.GetName().Name) )
    {
        return Assembly.GetExecutingAssembly();
    }
    return null;
}

private Assembly CurrentDomain_ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args)
{
    Assembly asb = AppDomain.CurrentDomain.GetAssemblies().Where(w => w.FullName == args.Name).FirstOrDefault();
    return asb;
}

Using the above code I can load the assembly even when it is not on the current directory of the main application (eg. Plugins folders).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM