Assembly.LoadFrom() 或 Assembly.Load() 能够删除文件
Posted
技术标签:
【中文标题】Assembly.LoadFrom() 或 Assembly.Load() 能够删除文件【英文标题】:Assembly.LoadFrom() or Assembly.Load() being able to delete file 【发布时间】:2017-01-02 12:04:11 【问题描述】:我的目标:
我正在编写一个 vsix(Visual Studio 扩展),我想在其中编译一个项目,然后加载生成的 .dll 并通过反射检查它。由于代码的编写方式,我无法使用ReflectionOnlyLoad()
。如果我只是简单地执行Assembly.Load
,那么文件将被锁定,直到用户重新启动整个 IDE。
我正在尝试根据我在网上找到的示例设置一个单独的 AppDomain。
它的要点是:
1. 我创建了一个代理类,它将跨AppDomain
实例编组数据:
internal class AppDomainProxy : MarshalByRefObject
public Assembly GetAssembly(string assemblyPath)
return Assembly.LoadFile(assemblyPath);
然后我创建它的一个实例:
var domaininfo = new AppDomainSetup ApplicationBase = System.Environment.CurrentDirectory, ShadowCopyDirectories = "true", ShadowCopyFiles = "true", LoaderOptimization = LoaderOptimization.MultiDomainHost ;
var adevidence = System.AppDomain.CurrentDomain.Evidence;
var domain = System.AppDomain.CreateDomain("reflection", adevidence, domaininfo);
var proxyType = new AppDomainProxy().GetType();
var proxyInstance = (AppDomainProxy)domain.CreateInstanceFromAndUnwrap(proxyType.Assembly.Location, proxyType.FullName);
var loadedAssembly = (proxyInstance as AppDomainProxy).GetAssembly(this._assemblyLocation);
这无法将我的透明代理转换为我的 AppDomainProxy
类型。
为了解决这个问题,可以很容易地提供这样的程序集解析器:
this.domain.AssemblyResolve += CurrentDomainOnAssemblyResolve;
private Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args)
var loadedAssemblies = System.AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in loadedAssemblies)
if (assembly.FullName == args.Name)
return assembly;
return null;
这很好用,我的代理是演员。
但是,当我调用我的方法时,CurrentDomainOnAssemblyResolve
再次被调用,向我暗示Assembly
属性并不是真正可序列化的,因此.net 只是试图在原始端加载程序集,从而导致同样的问题作为Assembly.Load
。这很容易看出,因为像 return AppDomain.Current.FriendlyName;
这样的简单 Microsoft 示例可以正常工作。
更新 作为一种解决方法,我只是移动了需要程序集在另一端(域内)运行的代码,然后将字符串返回,这可以很好地编组。不过,我会保持这个问题的开放性,因为我想知道是否有针对实际问题的解决方案。
【问题讨论】:
好吧,您又回到了第一方,因为程序集现在已加载到 both AppDomains 中,而您只能Unload()
其中一个。为什么不在您的辅助 AppDomain 中完成所有需要完成的工作?
@MickyD 根据问题的更新,这就是我最终要做的。但我不喜欢它:/
不喜欢?这就是 AppDomains 的全部意义所在。如果您创建一个子线程来执行冗长的任务但只是阻止等待它完成,那么创建一个用于隔离的 Appdomain 以加载一个程序集或插件只是为了将其返回到父域会以完全相同的方式破坏沙箱的目的.我建议您在 MSDN 杂志重新应用域上阅读一些好文章
【参考方案1】:
当然,您需要将需要与程序集一起使用的代码移动到单独的AppDomain
。没有任何魔法 - 如果您需要使用引用程序集中的类型,则需要加载该程序集。
跨AppDomain
边界(和其他远程处理方案)编组对象的主要方法有两种:要么制作对象的副本,要么编组所有方法调用。两者都非常棘手 - 您必须确保永远不会泄漏引用程序集中的类型,否则您需要将其加载到 both 域中。在您的情况下,您无法编组方法调用,因为 Assembly
不是(并且不能是)MarshalByRefObject
- 您的代理必须返回一个真正的 Assembly
对象,并且无法创建编组代理。
为了获得适当的隔离,您必须避免从您不想共享的程序集中泄漏任何类型,以及可能暴露任何这些的不可编组类型。如果您负担得起,这通常意味着坚持使用共享库中的原语和类型。保持良好的紧密接口,并尽量不要过多地利用“自动”编组 - 将自动代理返回到复杂对象很方便,但会使理解接口的范围变得更加困难。对象是否具有从非共享程序集中返回类型的方法?太糟糕了,您还需要将其加载到您的域中。
一般来说,使用多个实际互操作的 AppDomain(以及相关的 .NET 远程处理)是一件非常痛苦的事情。它很复杂,容易出错,而且很难做到正确。您可以阅读有关该主题的整本书。不再推荐使用它们是有原因的 - 以及为什么 PCL 不支持它们(而且很长一段时间以来,Mono 都不支持它们)。它们还有用吗?是的。软件隔离有很好的好处。但是你需要非常小心,不要把事情搞砸:)
【讨论】:
“它很复杂,容易出错,而且很难做到正确。您可以阅读有关该主题的整本书” - 也许。您可以对加密说同样的话;基于索赔的安全性; VS 语言服务,以及视觉识别。并不意味着我们不应该仅仅因为一些写得不好的书就放弃。至于 AD,我发现了一些很棒的 MSDN mag 大约 2006 年的文章,对我有很大帮助 @MickyD 是的,我并不是说你不应该使用它。我是说你需要学习很多东西,并确保你做对了。您会发现,对于很多调用应用程序域的情况,您实际上可以找到更好的解决方案。理想情况下,避免直接接触它们 - ASP.NET 程序员几乎从不需要考虑应用程序域,即使它们对 ASP.NET 程序非常重要。就像您不会编写自己的加密算法一样 - 您使用可以实际解决问题的最高抽象。 我会接受它,因为除了哲学观点之外,答案是正确的 -Assembly
不是 MarshalByRefObject
,因此在两个 App 域中都加载了。为了解决这个问题,我在子应用程序域中完成了所有工作。以上是关于Assembly.LoadFrom() 或 Assembly.Load() 能够删除文件的主要内容,如果未能解决你的问题,请参考以下文章
如果在 Windows 服务 .net 可执行文件中调用 .NET Assembly.Load/LoadFrom 会失败
Assembly.LoadFrom 在带有 MFC 的 cpp/CLI DLL 上失败,包括
Assembly.LoadFrom加载程序集类型转换失败解决方法