加载DLL创建实例和卸载

时间:2011-08-11 23:28:44

标签: c# wpf dll

我有一个应用程序将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.Children.Add(userControl);

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

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

如果我现在尝试使用上面的代码再次加载DLL(因为它同时改变了),我会收到一个错误,告诉我该文件正在使用中(File.Copy)。

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

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

修改

从我到目前为止阅读的评论(特别是来自svick)看起来我必须使用“AppDomain.CreateInstanceAndUnwrap”而不是“AppDomain.Load”。 我早先在搜索解决方案时已经看过这种方法,但正如svick提到的那样,因为UserControl不能从MarshalByRefObject继承而无效。

有没有人知道另一种方式?

3 个答案:

答案 0 :(得分:2)

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

我不确定是否有办法解决此问题,因为您无法从UserControl继承MarhshalByRefObject

答案 1 :(得分:1)

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

  

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

答案 2 :(得分:0)

也许来自MSDN文章AppDomain.Unload的这句话可以解释:

  

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

也许有一些流氓线程使应用程序域保持活动状态,因此也会保持文件锁定。

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