好的,这是交易:
我想将用户定义的Assembly
加载到我的AppDomain
,但是如果指定的Assembly
符合某些要求,我只想这样做。在我的情况下,除其他要求外,它必须具有Assembly
级Attribute
,我们可以调用MandatoryAssemblyAttribute
。
据我所知,我可以走两条路:
将Assembly
加载到我当前的AppDomain中,并检查Attribute
是否存在。容易但不方便,因为即使它没有MandatoryAssemblyAttribute
,我仍然坚持加载的装配。不好。
我可以创建一个新的AppDomain
并从那里加载Assembly
并检查我的旧MandatoryAddemblyAttribute
是否存在。如果是,则转储创建的AppDomain并继续将Assembly
加载到我的CurrentAppDomain
中,如果没有,只需转储新的AppDomain
,告诉用户,让他再试一次。
说起来容易做起来难。在网上搜索,我找到了几个关于如何解决这个问题的例子,包括在SO中发布的上一个问题:Loading DLLs into a separate AppDomain
我在这个解决方案中遇到的问题是,您实际上必须知道要加载的Assembly
中的类型(全名)。这不是我喜欢的解决方案。重点是尝试插入符合某些要求的任意Assembly
,并通过属性发现要使用的类型。事先不知道Assembly
将具有哪些类型。当然,我可以要求以这种方式使用的任何Assembly
应该实现一些虚拟类,以便为CreateInstanceFromAndUnwrap
提供“入口点”。我不愿意。
另外,如果我继续沿着这条线做点什么:
using (var frm = new OpenFileDialog())
{
frm.DefaultExt = "dll";
frm.Title = "Select dll...";
frm.Filter = "Model files (*.dll)|*.dll";
answer = frm.ShowDialog(this);
if (answer == DialogResult.OK)
{
domain = AppDomain.CreateDomain("Model", new Evidence(AppDomain.CurrentDomain.Evidence));
try
{
domain.CreateInstanceFrom(frm.FileName, "DummyNamespace.DummyObject");
modelIsValid = true;
}
catch (TypeLoadException)
{
...
}
finally
{
if (domain != null)
AppDomain.Unload(domain);
}
}
}
这样可以正常工作,但是如果那样的话,我会继续执行以下操作:
foreach (var ass in domain.GetAssemblies()) //Do not fret, I would run this before unloading the AppDomain
Console.WriteLine(ass.FullName);
我得到FileNotFoundException
。为什么呢?
我可以采取的另一条道路是:How load DLL in separate AppDomain 但我也没有任何运气。每当我选择一些随机的.NET FileNotFoundException
时,我都会得到一个Assembly
,除此之外它会失败,因为我需要知道程序集的名称(不是文件名)才能加载它而不是'符合我的要求。
还有另一种方法可以禁止MEF
(我不是针对.NET 3.5)吗?或者我是否因创建一些虚拟对象而无法加载Assembly
到CreateInstanceFromAndUnwrap
?如果是这样,为什么我不能在没有FileNotFoundException
的情况下迭代加载的程序集?我做错了什么?
非常感谢您的任何建议。
答案 0 :(得分:5)
我在这个解决方案中看到的问题是你实际上必须知道 程序集中的类型(全名)
这不太准确。您需要知道的是类型名称是某个程序集,不一定是您要检查的程序集。您应该在程序集中创建MarshalByRef类,然后使用 CreateInstanceAndUnwrap 从您自己的程序集(而不是您要检查的程序集)创建它的实例。然后该类将执行加载(因为它位于新的appdomain中)并检查并将布尔结果返回到原始appdomain。
这是一些让你前进的代码。这些类进入你自己的程序集(而不是你要检查的那个):
这个第一个类用于创建考试AppDomain并创建MarshalByRefObject类的实例(见底部):
using System;
using System.Security.Policy;
internal static class AttributeValidationUtility
{
internal static bool ValidateAssembly(string pathToAssembly)
{
AppDomain appDomain = null;
try
{
appDomain = AppDomain.CreateDomain("ExaminationAppDomain", new Evidence(AppDomain.CurrentDomain.Evidence));
AttributeValidationMbro attributeValidationMbro = appDomain.CreateInstanceAndUnwrap(
typeof(AttributeValidationMbro).Assembly.FullName,
typeof(AttributeValidationMbro).FullName) as AttributeValidationMbro;
return attributeValidationMbro.ValidateAssembly(pathToAssembly);
}
finally
{
if (appDomain != null)
{
AppDomain.Unload(appDomain);
}
}
}
}
这是实际存在于新AppDomain中的 MarshalByRefObject ,并将对程序集进行实际检查:
using System;
using System.Reflection;
public class AttributeValidationMbro : MarshalByRefObject
{
public override object InitializeLifetimeService()
{
// infinite lifetime
return null;
}
public bool ValidateAssembly(string pathToAssembly)
{
Assembly assemblyToExamine = Assembly.LoadFrom(pathToAssembly);
bool hasAttribute = false;
// TODO: examine the assemblyToExamine to see if it has the attribute
return hasAttribute;
}
}
答案 1 :(得分:3)
使用托管程序集阅读器(例如Mono.Cecil)可以轻松完成此操作。您可以检查程序集是否使用属性进行修饰,而无需在AppDomain中加载程序集,实际上,根本不会弄乱AppDomains。例如,塞西尔:
bool HasMandatoryAttribute (string fileName)
{
return AssemblyDefinition.ReadAssembly (fileName)
.CustomAttributes
.Any (attribute => attribute.AttributType.Name == "MandatoryAttribute");
}
这基本上是他所做的大多数插件系统,因为创建AppDomain并将其拆除对于运行时而言非常昂贵。