应用程序使用的 DLL 的热卸载/重新加载

Posted

技术标签:

【中文标题】应用程序使用的 DLL 的热卸载/重新加载【英文标题】:Hot Unload/Reload of a DLL used by an Application 【发布时间】:2011-02-03 15:08:14 【问题描述】:

我有一个加载 DLL 以执行特定处理部分的应用程序

Example : "Application.dll" loading "Process.dll" 

Process.dll 在运行时动态加载,使用反射,而不是在应用程序中引用。

处理完成后,DLL 需要在服务器上重新编译并稍后重新加载。 为了这样做,我需要释放它,否则我会收到以下消息: “无法将文件“Process.dll”复制到“Process.dll”。该进程无法访问文件“Process.dll”,因为它正在被另一个进程使用。”

所以问题是:如何在重新加载之前以编程方式从我的应用程序中释放/释放/卸载 Process.dll。当然,这样做的重点是停止应用程序。

编辑 1:

建议的解决方案如下:

AppDomain newDomain4Process = AppDomain.CreateDomain("newDomain4Process");
Assembly processLibrary = newDomain4Process.Load("Process.dll");
AppDomain.Unload(newDomain4Process);

我仍然遇到的问题是,虽然我给出了正确的完整路径,但我得到了 FileNotFound Exception。 this post的回答也没有达到预期的效果。

编辑 2:

This post救了我的命,代码如下:

class ProxyDomain : MarshalByRefObject
    
        public Assembly GetAssembly(string AssemblyPath)
        
            try
            
                return Assembly.LoadFrom(AssemblyPath);
            
            catch (Exception ex)
            
                throw ex;
            
        
    

   ProxyDomain pd = new ProxyDomain();
   Assembly a = pd.GetAssembly(FullDLLPath);

编辑 3: 我没有访问 AppDomain 并使用以前的解决方案卸载它。 当我使用 AppDomain Creation 的经典方法时,我感受到了 Alexei 的警告:AppDomain.Unload“似乎”工作,但程序集仍然加载(模块视图)。 所以我在某种程度上仍然有我的问题,因为我不能真正有效地卸载 DLL。

【问题讨论】:

关于您的编辑 3. 这敲响了警钟,我现在记得,当我尝试它时,由于类似的问题我放弃了,而是去创建一个非常简单的包装器可执行文件来加载 DLL 和我的原始可执行文件通过 COM 接口与 DLL 进行通信。然后,每当我需要加载新版本的 DLL 时,我就关闭了包装器可执行文件,因此我不需要担心 AppDomains。这对我来说没问题,因为性能并不那么重要,所以让 dll 退出进程并不会影响我:) 老兄哈哈。我即将放弃(至少暂时),因为已经两天了,我有被拉入深渊的印象。我的生活一直是痛苦的诅咒,因为我一直在试图解决我有大量错误/问题,这些错误/问题似乎很难在合理的时间内解决,并且没有深入挖掘概念以真正理解我正在做。暂时,我会休息一段时间,回到过去的常规编码,因为我有一个项目要交付:-) 注意权衡,编组(序列化)将在 AppDomain 之间发生。 ***.com/questions/5600761/… 您还可以做一件事来帮助减少以下过程的停机时间:等待旧 AppDomain 完成所有任务,卸载旧 AppDomain,复制新 DLL,然后加载正在使用的新 AppDomain Shadow Copy Assembiles。这消除了您获取的“无法复制文件...”错误,并让您将步骤的顺序更改为:复制新 dll,加载新 AppDomain 并开始处理新请求,等待旧 AppDomain 完成旧请求,卸载旧的 AppDomain。因此,现在您的停机时间为 0。 @StevensMiller 我也曾尝试过,除了我不担心卸载 DLL 我只是用新的 pdb/mdb 文件创建一个新的。我遇到了调试器随机不会在断点处中断等问题。你的想法听起来很有趣,你能详细说明一下吗?你是说这个'Shell' DLL 是 C++ 而你真正的 DLL 是 C#?那么您是在托管 DLL 上使用 LoadLibrary 吗?您在调试/单步执行您的真实 DLL 代码时遇到任何问题吗? 【参考方案1】:

自从我看到这个已经有一段时间了,但我很确定你需要创建一个新的AppDomain,然后在那里加载 DLL。

原因是您无法自行卸载Assembly,但您可以卸载包含AssemblyAppDomain

【讨论】:

正确,您不能从应用程序域“卸载”DLL 没错,一旦 AppDomain 中存在程序集,就无法将其卸载。 好吧。我的编辑正确吗?从技术上讲,这足以制作 Trick 吗?而且,顺便说一句,幕后发生了什么? AppDomain 是独立的,但仍然可以在彼此之间“通信”程序集? @Mika:对不起,几年没看这个了,所以不能评论你的代码 sn-p 除非说它看起来很合理。 @HansOlsson 提供的链接无效【参考方案2】:

一旦程序集被加载到AppDomain,它就不能被卸载。您必须处理掉AppDomain。如果您需要在应用程序中加载和卸载程序集,则需要生成一个新的 AppDomain 并加载程序集,然后在完成程序集后销毁 AppDomain

【讨论】:

【参考方案3】:

虽然您似乎根据您的编辑解决了问题,但这里有几个 cmets:

注意 Process.dll 中的对象“泄漏”到主 AppDomain 中。 IE。类似于抛出 Process.dll 中定义并在主 AppDomain 中捕获的自定义异常将强制 Process.dll 加载到主 AppDomain 中。执行您感兴趣的操作后,检查主 AppDomain 中已加载程序集的列表。

AppDomain 的卸载可能会失败并且可能需要很长时间。

【讨论】:

嘻嘻嘻!我陷入了泄漏问题!伙计,这是个坏消息。 Module 视图显示该死的程序集仍然被加载。有什么办法可以强迫它出去吗? 你不能强行把它拿出来——只要它特别是 AppDomain 被加载,它就会留在那里。您必须避免该程序集的类型跨越 AppDomain 边界。确保您不返回该程序集中定义的任何对象。 IE。如果您从主程序集中返回实现接口的对象,您可能需要在主程序集中编写代理类并返回它。预计将花费大量时间将程序集加载到新的 AppDomain 权利。

以上是关于应用程序使用的 DLL 的热卸载/重新加载的主要内容,如果未能解决你的问题,请参考以下文章

在 C# 中动态加载和使用 DLL

完整的动态加载卸载程序集的解决方案

在运行时加载、使用和卸载程序集

使用AppDomain进行动态加载和卸载dll

C#中如何动态加载和卸载DLL

C#动态加载dll 时程序集的卸载问题