我在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是否与加载它的程序集的签名相匹配。
如果需要更清晰,请告诉我。
非常感谢!
答案 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集合中,则不再加载“插件”。
当一些插件开始相互依赖时,它发生的最多,并且装配器装载器在装载它们时会反复加载相同的装配件。
希望它有助于解决你的问题,它确实让我发疯了!