Reputation: 388
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?
Upvotes: 2
Views: 436
Reputation: 388
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).
Upvotes: 1
Reputation: 1864
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;
}
Upvotes: 2