动态加载插件体系结构的程序集

时间:2012-05-07 13:57:28

标签: c# plugins .net-assembly

我在C#.NET中构建了一个插件架构,可以从预定义的物理文件路径动态加载DLL。我知道可能存在于两个位置的内存位置的程序集,因此使用类似的东西验证程序集中的成员(接口)是不可靠的。

if(plugin is T)
    // cache the assembly

...所以目前我正在使用接口名称进行比较,然后从中激活一个实例。但是,这也有局限性,因为接口'IPlugin'是许多第三方程序集使用的非常常见的接口名称(即log4net等)

采用以下代码(不起作用):

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);
            }
    }
}

我的问题:验证动态加载的DLL与IPlugin接口匹配的最佳(可靠)方法是什么?

一种想法是硬编码IPlugin的公钥令牌并验证,但我想知道是否有更正式的方法。作为一个例子,我可以想象一个潜在的安全漏洞,其中包含欺骗IPlugin名称或公钥令牌的程序集...所以也许有一种很好的方法来测试加载的DLL是否与加载它的程序集的签名相匹配。

如果需要更清晰,请告诉我。

非常感谢!

4 个答案:

答案 0 :(得分:3)

我这样解决:

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;
}

我称之为:

List<MyPlugin> plugins = LoadPluginsFromPath<MyPlugin>( "plugins" );

答案 1 :(得分:2)

列举汇编中所有类型的Insteas,为什么不定义工厂类?

工厂类将有一个更合适的名称,例如“YourFramework.PluginTypeFactory”,确实消除了可能的名称冲突。

此外,Assembly.GetTypes可能fail对某些程序集非常严重,并且会在bug程序集上花费大量时间。

答案 2 :(得分:1)

使用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使用一种类型来查看是否可以从中分配另一种类型。它完全考虑了实际类型,而不仅仅是类型的名称。因此,即使您的程序集或其他程序集包含名为IPlugin的类型,也只能找到yourIPlugin中的类型。

答案 3 :(得分:1)

我碰巧遇到了和你一样的问题,一些“插件”加载了两次,.NET Framework在使用时无法解析类型

IsAssignableFrom

我解决了它为AppDomain的AssemblyResolve事件添加处理程序,如果它已经加载到当前AppDomain的Assemblies集合中,则不再加载“插件”。

当一些插件开始相互依赖时,它发生的最多,并且装配器装载器在装载它们时会反复加载相同的装配件。

希望它有助于解决你的问题,它确实让我发疯了!