Assembly.LoadFrom()或Assembly.Load()能够删除文件

时间:2017-01-02 12:04:12

标签: c# .net-assembly appdomain

我的目标: 我正在编写一个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样本工作得很好。

更新作为一种解决方法,我只是移动了我需要程序集在另一端(域内)运行的代码,然后返回编组好的字符串。不过,我会保持这个问题,因为我想知道是否有实际问题的解决方案。

1 个答案:

答案 0 :(得分:0)

当然,您需要将需要使用程序集的代码移动到单独的AppDomain。没有任何魔法 - 如果你需要使用引用程序集中的类型,你需要加载该程序集。

AppDomain边界(以及其他远程处理方案)中编组对象的主要方法有两种:要么创建对象的副本,要么编组所有方法调用。两者都非常棘手 - 您必须确保永远不会泄漏引用程序集中的类型,否则您需要将其加载到两个域中。在您的情况下,您无法封送方法调用,因为Assembly (并且不能)MarshalByRefObject - 您的代理必须返回真实的Assembly对象,并且无法创建编组代理。

要获得适当的隔离,必须避免从您不想共享的程序集中泄漏任何类型,以及可能暴露任何类型的非可编组类型。这通常意味着如果你负担得起,就可以从共享库中坚持原语和类型。保持一个很好的紧密接口,并尽量不要过多地利用“自动”编组 - 将一个自动代理返回到一个复杂的对象很方便,但是要让它更难理解界面的范围。该对象是否具有从非共享程序集返回类型的方法?太糟糕了,您还需要在您的域中加载它。

通常,使用多个实际互操作的AppDomain(以及相关的.NET远程处理)是一个巨大的痛苦。它很复杂,容易出错,很难做对。您可以阅读有关该主题的全书。有一个原因是为什么不再推荐使用它们 - 以及PCL不支持它们的原因(很长一段时间,Mono都没有支持)。它们仍然有用吗?是啊。软件隔离具有非常好的好处。但你需要非常小心,不要搞砸了:)