Ive built a plugin architecture in C#.NET that dynamically loads DLLs from a predefined physical file path. I am aware of assemblies possibly existing in memory location in two locations thus making it unreliable to verify a member (interface) in the assembly using something like...
if(plugin is T)
// cache the assembly
... so currently I am using the interface name to compare, then activating an instance from that. But, this has limitations too because the interface 'IPlugin' is a very common interface name which lots of third party assemblies use, (i.e. log4net, etc)
Take the following code (which is does not work):
foreach (Type t in assembly.GetTypes())
{
type = t;
if (!type.IsInterface)
{
var plugin = type.GetInterface(typeof(T).Name);
if (plugin != null)
if (plugin is T)
{
T p = (T)Activator.CreateInstance(type);
if (!this.Plugins.Select(sp => sp.Name).Contains(p.Name))
this.Plugins.Add(p);
}
}
}
My question: What is the best (reliable) way to verify the dynamically loaded DLL matches the IPlugin interface?
One thought is to hard code the IPlugin's public key token and verify that, but I am wondering if there is a more formal way. As an example, I can imagine a potential security hole with assemblies spoofing the IPlugin name, or public key token... so perhaps there is a good way to test that the DLL loaded matches the signature of the assembly loading it.
Please let me know if more clarity is required.
Very grateful!
I solve it like so:
public List<T> LoadPluginsFromPath<T>( string Path ) {
List<T> results = new List<T>();
DirectoryInfo Directory = new DirectoryInfo( Path );
if ( !Directory.Exists ) {
return results; // Nothing to do here
}
FileInfo[] files = Directory.GetFiles( "*.dll" );
if ( files != null && files.Length > 0 ) {
foreach ( FileInfo fi in files ) {
List<T> step = LoadPluginFromAssembly( fi.FullName );
if ( step != null && step.Count > 0 ) {
results.AddRange( step );
}
}
}
return results;
}
private List<T> LoadPluginFromAssembly<T>( string Filename ) {
List<T> results = new List<T>();
Type pluginType = typeof( T );
Assembly assembly = Assembly.LoadFrom( Filename );
if ( assembly == null ) {
return results;
}
Type[] types = assembly.GetExportedTypes();
foreach ( Type t in types ) {
if ( !t.IsClass || t.IsNotPublic ) {
continue;
}
if ( pluginType.IsAssignableFrom( t ) ) {
T plugin = Activator.CreateInstance( t ) as T;
if ( plugin != null ) {
results.Add( plugin );
}
}
}
return results;
}
I call it like so:
List<MyPlugin> plugins = LoadPluginsFromPath<MyPlugin>( "plugins" );
Use IsAssignableFrom:
var yourIPlugin = typeof(IPlugin);
foreach (Type t in assembly.GetTypes())
{
if (yourIPlugin.IsAssignableFrom(t))
{
T p = (T)Activator.CreateInstance(t);
if (!this.Plugins.Select(sp => sp.Name).Contains(p.Name))
this.Plugins.Add(p);
}
}
IsAssignableFrom uses a type to see if another type can be assigned from it. It takes fully into consideration the actual type, not just the name of the type. So even if your assembly, or other assemblies, contain types named IPlugin, only the one from yourIPlugin would be found.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With