C# 如何从主AppDomain卸载程序集?

C# 如何从主AppDomain卸载程序集?,c#,.net,appdomain,C#,.net,Appdomain,我想知道如何卸载加载到主AppDomain中的程序集 我有以下代码: var assembly = Assembly.LoadFrom( FilePathHere ); 完成后,我需要/希望能够卸载此程序集 感谢您的帮助。对于.net core 3.0及更高版本: 现在可以卸载程序集。请注意,appdomains在.net core中不再可用。相反,您可以创建一个或多个AssemblyLoadContext,通过该上下文加载程序集,然后卸载该上下文。看,或者 适用于.net core 3之前的

我想知道如何卸载加载到主AppDomain中的程序集

我有以下代码:

var assembly = Assembly.LoadFrom( FilePathHere );
完成后,我需要/希望能够卸载此程序集


感谢您的帮助。

对于.net core 3.0及更高版本:

现在可以卸载程序集。请注意,appdomains在.net core中不再可用。相反,您可以创建一个或多个AssemblyLoadContext,通过该上下文加载程序集,然后卸载该上下文。看,或者

适用于.net core 3之前的.net版本,包括netframework 4及更低版本

无法从appdomain卸载程序集。您可以销毁appdomain,但一旦将程序集加载到appdomain中,它将在appdomain的生命周期中一直存在

参见Jason Zander对


如果您使用的是3.5,那么可以使用AddIn框架使管理/调用不同的AppDomain变得更容易(您可以卸载AppDomain,卸载所有程序集)。如果您使用的是之前的版本,则需要自己创建一个新的appdomain来卸载它。

如果不卸载整个appdomain,则无法卸载程序集:

  • 您正在应用程序域中运行该代码。这意味着可能存在一些呼叫站点和呼叫堆栈,其中包含地址,希望继续工作

  • 假设您确实设法跟踪程序集已运行代码的所有句柄和引用。假设您没有加密代码,一旦成功释放了程序集,您就只释放了元数据和IL。JIT代码仍然在应用程序域加载器堆中分配(JIT方法按照调用顺序在缓冲区中顺序分配)

  • 最后一个问题涉及已加载共享的代码,否则更正式地称为“与域无关”(在ngen工具上签出/共享)。在此模式下,程序集的代码将从任何应用程序域(无硬连线)生成以执行

  • 建议您自然地围绕应用程序域边界设计应用程序,其中完全支持卸载


    如果您想拥有可以在以后卸载的临时代码,根据您的需要,
    DynamicMethod
    类可能会执行您想要的操作。但是,这不会给您提供类。

    您应该将临时程序集加载到另一个
    AppDomain
    中,当不使用时,您可以卸载该
    AppDomain
    。它既安全又快速。

    下面是一个很好的示例,说明如何在运行时编译和运行dll,然后卸载所有资源:

    我知道它很古老,但可能会帮助别人。您可以从流中加载文件并释放它。这对我有用。我找到了解决办法


    希望能有所帮助。

    我也知道这是一个很老的问题,但可能会帮助有这个问题的人! 这是我找到的一种方法! 而不是使用:

    var assembly = Assembly.LoadFrom( FilePathHere );
    
    使用以下命令:

    var assembly = Assembly.Load( File.ReadAllBytes(FilePathHere));
    
    这实际上是加载程序集文件的“内容”,而不是加载文件本身。这意味着部件文件上没有放置文件锁!因此,现在可以复制、删除或升级它,而无需关闭应用程序或尝试使用单独的AppDomain或封送

    优点:用一行代码修复非常简单! 缺点:无法使用AppDomain、Assembly.Location或Assembly.CodeBase

    现在您只需要销毁在程序集上创建的任何实例。 例如:

    assembly = null;
    

    另一种选择是,如果程序集刚刚加载,要检查程序集的信息(如公钥),更好的方法是不加载它,而是先加载AssemblyName来检查信息:

    AssemblyName an = AssemblyName.GetAssemblyName ("myfile.exe");
    byte[] publicKey = an.GetPublicKey();
    CultureInfo culture = an.CultureInfo;
    Version version = an.Version;
    
    编辑

    如果您需要反映程序集中的类型而不将程序集放入应用程序域,则可以使用
    assembly.ReflectionOnlyLoadFrom
    方法。 这将允许您查看程序集中的类型,但不允许您实例化它们,也不会将程序集加载到AppDomain中

    将此示例视为exlanation

    public void AssemblyLoadTest(string assemblyToLoad)
    {
        var initialAppDomainAssemblyCount = AppDomain.CurrentDomain.GetAssemblies().Count(); //4
    
        Assembly.ReflectionOnlyLoad(assemblyToLoad);
        var reflectionOnlyAppDomainAssemblyCount = AppDomain.CurrentDomain.GetAssemblies().Count(); //4
    
        //Shows that assembly is NOT loaded in to AppDomain with Assembly.ReflectionOnlyLoad
        Assert.AreEqual(initialAppDomainAssemblyCount, reflectionOnlyAppDomainAssemblyCount); // 4 == 4
    
        Assembly.Load(assemblyToLoad);
        var loadAppDomainAssemblyCount = AppDomain.CurrentDomain.GetAssemblies().Count(); //5
    
        //Shows that assembly is loaded in to AppDomain with Assembly.Load
        Assert.AreNotEqual(initialAppDomainAssemblyCount, loadAppDomainAssemblyCount); // 4 != 5
    }
    

    我认为.Net 3.5改变了这一点?没有计划允许从应用程序域卸载程序集(我也不希望他们这样做,这很难做到,关心的人会使用更安全、更干净的解决方法)。你能做的最好的是轻量级代码生成器,Khoth提到不要使用LoadFrom,它与Load上下文处于不同的上下文中,可能会导致问题。这个问题问得好,但我看不到关于如何解析var assembly=assembly.LoadFrom(FilePathHere)的任何明确答案;请参见AppDomain.docCallback和MarshalByRefObject:您使用一个方法创建MarshalByRefObject“invoker”,该方法执行您想要的操作并在其可变字段中返回某些内容,您创建一个临时AppDomain并调用dom.DoCallback(invoker.DoSomething)以使该对象执行您想要的操作;然后从appdomain中的调用程序收集结果并卸载临时域。@jkff:不幸的是,如果使用MarshallByRefObject,如乒乓球示例中所示,Assembly.LoadFrom将像在主appdomain中一样执行,因此卸载临时appdomain仍会产生无法卸载程序集的旧问题。我认为,如果您想访问临时AppDomain并仍然能够卸载程序集,您可能必须以某种方式从临时AppDomain序列化所需的数据,而不进行封送处理。@jkff:您可能是对的。。。但它并不像MSDN示例中描述的那样简单。可能应该使用tempAppDomain.CreateInstanceAndUnwrap。。。甚至可能需要使用AppDomain.GetData和SetData。(如果appdomain的唯一用途是加载和卸载,那么它不会真正扰乱appdomain。)“不能使用appdomain”是指不能在该程序集中启动appdomain,或者可以更具体一些?那怎么办