加载DLL、创建实例和卸载

4
我有一个应用程序,它会将一个名为UserControlLibrary的DLL文件复制到自己的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的东西,但不幸的是我无法在这个项目中实现它。希望能提供一个示例或修改上面的代码。
编辑:
从我迄今为止阅读的评论中(特别是来自svick的评论),看起来我必须使用“AppDomain.CreateInstanceAndUnwrap”而不是“AppDomain.Load”。我在搜索解决方案时已经看到过这种方法,但正如svick所提到的,这行不通,因为UserControl无法继承自MarshalByRefObject。
有人知道其他方法吗?

请不要在标题前加上"C#"。我们在[SO]上使用标签来代替。 - John Saunders
你的类型可能会跨越域边界,因此它们将被困在“main”运行的应用程序域中。使用MarhshalByRefObject建议以及远程对象的常见接口。这很难,但一旦你理解了,你就会明白 :) 顺便说一句,这是学习远程调用的好练习。 - leppie
用户控件无法继承自MarhshalByRefObject...? - MemphiZ
3个回答

2

阅读AppDomain.Load()文档。具体说明了该方法将程序集同时加载到调用该方法的程序集和当前程序集中。因此,即使您卸载了应用程序域,程序集仍然会在当前程序集中保持加载状态。

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


是的。我使用AppDomain.CreateInstanceAndUnwrap时也遇到了同样的问题。 - MemphiZ
那么,无法使用AppDomain.CreateInstanceAndUnwrap将程序集加载到不同的AppDomain中以用于WPF用户控件吗? - NoWar
据我所知,是的,没有办法做到这一点。 - svick

1

当您调用卸载操作(unload)时,应用程序域(AppDomain)不会立即卸载(请参阅MSDN的完整文章):

当线程调用Unload时,目标域标记为正在卸载。专用线程会尝试卸载该域,并终止该域中的所有线程。如果某个线程未终止,例如因为它正在执行非托管代码或正在执行finally块,则在一段时间后将引发CannotUnloadAppDomainException异常...


有趣的是如何处理实际的AppDomain卸载。 - sll

0

也许这篇AppDomain.Unload的MSDN文章中的一句话可以解释问题:

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

也许有一些流氓线程正在保持应用程序域的活动状态,从而也使您的文件被锁定。

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


那么你会说我的usercontrols实例的使用没有问题吗?它没有被加载到主AppDomain中吗? 附注:我目前正在使用你提到的解决方法。 - MemphiZ
很难知道;你的控件是否会执行一些像线程生成这样的棘手操作? - Jacob
不,它们完全独立运行,目前还没有互相通信。 - MemphiZ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接