Assembly.LoadFrom() 或 Assembly.Load() 可以删除文件
Assembly.LoadFrom() or Assembly.Load() being able to delete file
我的目标:
我正在编写一个 vsix(Visual Studio 扩展名),我想在其中编译一个项目,然后加载生成的 .dll 并通过反射检查它。由于代码的编写方式,我无法使用 ReflectionOnlyLoad()
。如果我只是做 Assembly.Load
然后文件被锁定,直到用户重新启动整个 IDE.
我正在尝试根据我在网上找到的示例设置一个单独的 AppDomain。
它的要点是:
1. 我创建了一个代理 class,它将跨 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 示例工作得很好。
UPDATE 作为解决方法,我只是将需要程序集的代码移至另一侧(域内)的 运行,然后 return 元帅罚款的字符串。不过,我会保留这个问题,因为我想知道是否有针对实际问题的解决方案。
嗯,当然你需要将需要与程序集一起工作的代码移动到单独的 AppDomain
。没有魔法 - 如果您需要使用引用程序集中的类型,则需要加载该程序集。
有两种主要方式可以跨 AppDomain
边界(和其他远程场景)编组对象:创建对象的副本,或者编组所有方法调用。两者都非常棘手 - 您必须确保永远不会泄漏引用程序集中的类型,否则您需要将其加载到 both 域中。在您的情况下,您无法编组方法调用,因为 Assembly
是 而不是 (并且不能是) MarshalByRefObject
- 您的代理必须 return真正的 Assembly
对象,无法创建编组代理。
要获得适当的隔离,您必须避免从您不想共享的程序集中泄漏任何类型,以及可能暴露任何这些类型的不可编组类型。如果您负担得起,这通常意味着坚持使用共享库中的原语和类型。保持一个很好的紧密接口,并尽量不要过多地利用 "automatic" 编组 - return 对复杂对象进行自动代理很方便,但会使理解接口的范围变得更加困难。该对象是否具有 return 非共享程序集中的类型的方法?太糟糕了,你也需要在你的域中加载它。
一般来说,使用多个实际互操作的 AppDomain(以及相关的 .NET 远程处理)是一件非常痛苦的事情。它很复杂,容易出错,而且很难做到正确。您可以阅读有关该主题的整本书。不再推荐使用它们是有原因的——以及为什么 PCL 不支持它们(并且很长一段时间,Mono 不支持它们)。它们还有用吗?是的。软件隔离有很好的好处。但是你需要非常小心,不要把事情搞砸了:)
我的目标:
我正在编写一个 vsix(Visual Studio 扩展名),我想在其中编译一个项目,然后加载生成的 .dll 并通过反射检查它。由于代码的编写方式,我无法使用 ReflectionOnlyLoad()
。如果我只是做 Assembly.Load
然后文件被锁定,直到用户重新启动整个 IDE.
我正在尝试根据我在网上找到的示例设置一个单独的 AppDomain。
它的要点是:
1. 我创建了一个代理 class,它将跨 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 示例工作得很好。
UPDATE 作为解决方法,我只是将需要程序集的代码移至另一侧(域内)的 运行,然后 return 元帅罚款的字符串。不过,我会保留这个问题,因为我想知道是否有针对实际问题的解决方案。
嗯,当然你需要将需要与程序集一起工作的代码移动到单独的 AppDomain
。没有魔法 - 如果您需要使用引用程序集中的类型,则需要加载该程序集。
有两种主要方式可以跨 AppDomain
边界(和其他远程场景)编组对象:创建对象的副本,或者编组所有方法调用。两者都非常棘手 - 您必须确保永远不会泄漏引用程序集中的类型,否则您需要将其加载到 both 域中。在您的情况下,您无法编组方法调用,因为 Assembly
是 而不是 (并且不能是) MarshalByRefObject
- 您的代理必须 return真正的 Assembly
对象,无法创建编组代理。
要获得适当的隔离,您必须避免从您不想共享的程序集中泄漏任何类型,以及可能暴露任何这些类型的不可编组类型。如果您负担得起,这通常意味着坚持使用共享库中的原语和类型。保持一个很好的紧密接口,并尽量不要过多地利用 "automatic" 编组 - return 对复杂对象进行自动代理很方便,但会使理解接口的范围变得更加困难。该对象是否具有 return 非共享程序集中的类型的方法?太糟糕了,你也需要在你的域中加载它。
一般来说,使用多个实际互操作的 AppDomain(以及相关的 .NET 远程处理)是一件非常痛苦的事情。它很复杂,容易出错,而且很难做到正确。您可以阅读有关该主题的整本书。不再推荐使用它们是有原因的——以及为什么 PCL 不支持它们(并且很长一段时间,Mono 不支持它们)。它们还有用吗?是的。软件隔离有很好的好处。但是你需要非常小心,不要把事情搞砸了:)