1

我在 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

4 回答 4

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" );
于 2012-05-07T14:05:55.000 回答
2

除了枚举汇编中的所有类型,你为什么不定义一个工厂类?

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

此外,在某些程序集上Assembly.GetTypes可能严重失败,并在错误程序集上花费大量时间。

于 2012-05-07T15:33:38.953 回答
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的类型,也只会找到 from 之一yourIPlugin

于 2012-05-07T14:01:27.327 回答
1

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

IsAssignableFrom

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

当一些插件开始相互依赖并且程序集加载器在它们已经加载时一遍又一遍地加载相同的程序集时,这种情况发生得最多。

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

于 2012-05-07T15:37:46.673 回答