【问题标题】:Load DLL create Instance and Unload加载 DLL 创建实例并卸载
【发布时间】:2011-08-11 23:28:43
【问题描述】:

我有一个应用程序,它将一个 DLL (UserControlLibrary) 复制到它自己的 Debug/Release 文件夹并使用此代码加载它:

AppDomain appDomain = AppDomain.CreateDomain("MyDomain");
OpenFileDialog dialog = new OpenFileDialog();
dialog.Filter = "DLLs (*.dll)|*.dll";
if (dialog.ShowDialog().Value)
{
    string newLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\" + dialog.SafeFileName;
    File.Copy(dialog.FileName, newLocation, true);
    Assembly assembly = appDomain.Load(AssemblyName.GetAssemblyName(newLocation));
    UserControl userControl = (UserControl) assembly.CreateInstance("WpfControlLibrary1.UserControl1");
}

我现在将该 UserControl 添加到 Grid 中:

grid.Children.Add(userControl);

工作正常。现在我尝试使用以下方法卸载 DLL:

AppDomain.Unload(appDomain);
grid.Children.Clear();

如果我现在尝试使用上述代码再次加载 DLL(因为它同时发生了变化),我会收到一条错误消息,告诉我文件正在使用中 (File.Copy)。

我已经阅读了很多内容,我的猜测是我不能像以前那样使用 UserControl(因为它会加载到主 AppDomain 中)。我必须如何更改代码才能使其正常工作?

我也阅读了很多关于使用 MarshalByRefObject 的内容,但不幸的是我无法在这个项目中实现它。上面代码的示例或修改会很好。

编辑:

从我目前读到的 cmets(尤其是 svick)看来,我必须使用“AppDomain.CreateInstanceAndUnwrap”而不是“AppDomain.Load”。 我之前在寻找解决方案时已经看到了这种方法,但正如 svick 提到的那样,这不起作用,因为 UserControl 不能从 MarshalByRefObject 继承。

有人知道另一种方法吗?

【问题讨论】:

  • 请不要在标题前加上“C#”。我们在Stack Overflow 上使用标签。
  • 您的类型可能跨越了域边界,因此它们将卡在“主”运行的应用程序域中。使用MarhshalByRefObject 建议以及远程对象的通用接口。这很难,但一旦你掌握了它,你就会明白 :) 顺便说一句,这是学习远程处理的一个很好的练习。
  • UserControl 不能从 MarhshalByRefObject 继承...?

标签: c# wpf dll


【解决方案1】:

阅读the documentation for AppDomain.Load()。它特别指出,此方法将程序集 both 加载到您正在调用该方法的程序集中并加载到当前程序集中。因此,即使您卸载了应用程序域,程序集仍会在当前程序集中保持加载状态。

我不确定有没有办法解决这个问题,因为你不能让 UserControl 继承自 MarhshalByRefObject

【讨论】:

  • 是的。我在使用 AppDomain.CreateInstanceAndUnwrap 时也遇到了同样的问题。
  • 那么没有办法使用 AppDomain.CreateInstanceAndUnwrap 让 WPF UserControls 将程序集加载到不同的 AppDomain 中?
  • @Peretz 据我所知,是的,没有办法做到这一点。
【解决方案2】:

当您调用 unload 时,AppDomain 不会立即卸载 (see MSDN for the full article):

当线程调用Unload时,目标域被标记为卸载。 专用线程尝试卸载域,并且所有线程 域被中止。例如,如果一个线程没有中止 因为它正在执行非托管代码,或者因为它正在执行 finally块,然后过一段时间a 抛出CannotUnloadAppDomainException...

【讨论】:

  • 有趣的是如何处理 AppDomain 的实际卸载
【解决方案3】:

也许 AppDomain.Unload 的 MSDN 文章中的这句话可以解释一些事情:

在 .NET Framework 2.0 版中,有一个专门用于卸载应用程序域的线程。这提高了可靠性,尤其是在托管 .NET Framework 时。当线程调用 Unload 时,目标域被标记为卸载。专用线程尝试卸载域,并且域中的所有线程都被中止。如果线程没有中止,例如因为它正在执行非托管代码,或者因为它正在执行 finally 块,那么在一段时间后,在最初调用 Unload 的线程中会抛出一个 CannotUnloadAppDomainException。如果无法中止的线程最终结束,则不会卸载目标域。因此,在 .NET Framework 2.0 版中,域不能保证卸载,因为它可能无法终止正在执行的线程。

也许有一些恶意线程使应用程序域保持活动状态,因此也使您的文件处于锁定状态。

作为一种解决方法,您可以将 DLL 复制到随机文件名。

【讨论】:

  • 所以你会说我对 usercontrols 实例的使用没有问题?它没有被加载到主 AppDomain 中? P.S.:我目前正在使用您提到的解决方法。
  • 很难知道;你的控件会做一些棘手的事情吗,比如线程生成?
  • 没有。他们完全独立,彼此之间不交流(目前)。
猜你喜欢
  • 1970-01-01
  • 2020-09-24
  • 2011-01-27
  • 2012-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-20
相关资源
最近更新 更多