private static void Main() { const string fullPath = "C:\\Projects\\AppDomains\\distrib\\MyLibrary.dll"; // Starting out with a version of MyLibrary.dll which only has 1 method,named Foo() AssemblyName assemblyName = AssemblyName.GetAssemblyName(fullPath); AppDomain appDomain = AppDomain.CreateDomain("MyTemp"); appDomain.Load(assemblyName); appDomain.DomainUnload += appDomain_DomainUnload; AppDomain.Unload(appDomain); // Breakpoint here; swap out different version of MyLibrary.dll which only has 1 method,named Goo() AssemblyName assemblyName2 = AssemblyName.GetAssemblyName(fullPath); AppDomain appDomain2 = AppDomain.CreateDomain("MyTemp2"); Assembly asm2 = appDomain2.Load(assemblyName2); foreach (Type type in asm2.GetExportedTypes()) { foreach (MemberInfo memberInfo in type.GetMembers()) { string name = memberInfo.Name; // Breakpoint here: Found Foo and but no Goo! I was expecting Goo and no Foo. } } } private static void appDomain_DomainUnload(object sender,EventArgs e) { // This gets called before the first breakpoint }
编辑:
好的,这显然是我第一次发帖.感谢Daniel格式化我的代码(我现在看到工具栏按钮,以及预览窗格!).我没有找到一种方法来回复那些要求澄清原始帖子或1个答案的人回复“评论”,所以我只是发布另一个“答案”来保持对话的进行. (赞赏如何完成评论的指针将不胜感激).
对帖子的评论:
Mitch – 陷入了火焰之中,因为我的foreach循环应该迭代修改后的DLL中的类型,而不是先前加载/卸载的类型.
马修 – 这可能会奏效,但我真的需要相同文件名的情况才能奏效.
迈克二 – 没有强烈的名字.
评论答案:
蓝色& Mike Two – 我会提出你的建议,但首先我需要了解一个关键方面.我已经读过你必须注意不要将程序集拉入主应用程序域,并且代码以前在卸载之前有一个foreach循环的副本.因此,怀疑访问MethodInfos是将程序集吸入主应用程序域,我删除了循环.那时我知道我需要寻求帮助,因为第一个DLL仍然无法卸载!
所以我的问题是:以下代码段中的内容导致主应用程序域直接(或间接)访问dll中的任何内容…为什么它会导致程序集也加载到主应用程序域中:
appDomain.Load(assemblyName); appDomain.DomainUnload += appDomain_DomainUnload; AppDomain.Unload(appDomain);
没有给我很大的信心我实际上能够在卸载之前使用DLL.
编辑2:
Mike Two – 感谢您的坚持……从DoCallBack中加载程序集是秘诀.对于任何感兴趣的人,这里有一些可能有用的信息:
听起来没有人能真正重现我的条件.为了演示原始问题,我以这种方式生成了我的dll:1.将类库项目添加到解决方案2.使用Foo构建版本1.0.0;将生成的程序集重命名为MyLibrary.dll.f. 3.将Foo重命名为Goo并构建另一个版本1.0.0;将生成的程序集重命名为MyLibrary.dll.g. 4.从解决方案中删除项目.在开始运行之前,我删除了.f并运行到断点(卸载后的第一行).然后我重新启动了.f并从另一个dll中移除了.g并运行到下一个断点. Windows并没有阻止我重命名.注意:虽然这是更好的练习,但我没有更改版本号,因为我不想假设我的客户总是这样,因为AssemblyInfo中的默认条目不是通配符版本.这似乎是一个丑陋的案件.
此外,我刚刚发现了一些可以早日让我了解的东西:
AssemblyName assemblyName = AssemblyName.GetAssemblyName(FullPath); AppDomain appDomain = AppDomain.CreateDomain("MyTemp"); appDomain.Load(assemblyName); Assembly[] tempAssemblies = appDomain.GetAssemblies(); // MyLibrary.dll has been loaded into the temp domain...good Assembly[] mainAssemblies = AppDomain.CurrentDomain.GetAssemblies(); // MyLibrary.dll has been loaded into the main domain,too...bad!
所以我不确定AppDomain.Load的重点是什么,但它似乎也有加载程序集到主应用程序域的额外副作用.使用这个实验,我可以看到Mike Two的解决方案只将它加载到temp域中:
AppDomain appDomain = AppDomain.CreateDomain("MyTemp"); appDomain.DoCallBack(CallBackDelegate); // Executes Assembly.LoadFrom Assembly[] tempAssemblies = appDomain.GetAssemblies(); // MyLibrary.dll has been loaded into the temp domain...good Assembly[] mainAssemblies = AppDomain.CurrentDomain.GetAssemblies(); // MyLibrary.dll has NOT been loaded into the main domain...great!
那么,Mike Two,这个StackOverflow新手究竟是如何将你的答案标记为“已接受”的?或者我可以不这样做,因为我只是这里的客人?
现在我要学习如何在不将程序集吸入主应用程序域的情况下实际使用MyLibrary.谢谢大家的参与.
编辑3:
BlueMonkMN – 我继续前进并取消了活动订阅并得到了相同的结果.完整的程序现在列在下面:
const string fullPath = "C:\\Projects\\AppDomains\\distrib\\MyLibrary.dll"; // Starting out with a version of MyLibrary.dll which only has 1 method,named Foo() AssemblyName assemblyName = AssemblyName.GetAssemblyName(fullPath); AppDomain appDomain = AppDomain.CreateDomain("MyTemp"); appDomain.Load(assemblyName); AppDomain.Unload(appDomain); // Breakpoint here; swap out different version of MyLibrary.dll which only has 1 method,named Goo() AssemblyName assemblyName2 = AssemblyName.GetAssemblyName(fullPath); AppDomain appDomain2 = AppDomain.CreateDomain("MyTemp2"); Assembly asm2 = appDomain2.Load(assemblyName2); foreach (Type type in asm2.GetExportedTypes()) { foreach (MemberInfo memberInfo in type.GetMembers()) { string name = memberInfo.Name; // Breakpoint here: Found Foo and but no Goo! I was expecting Goo and no Foo. } }
似乎应该没有办法将程序集拉入这两行之间的主应用程序域:
appDomain.Load(assemblyName); AppDomain.Unload(appDomain);
解决方法
我在卸载调用后放了一个断点.然后我尝试在第一个版本的顶部复制第二个版本.我认为这就是你正在做的事情,因为路径永远不会改变. Windows不允许我在版本1上复制版本2.dll已被锁定.
我使用DoCallback更改了代码以使用在AppDomain中执行的代码加载dll.那很有效.我可以交换dll并找到新方法.这是代码.
class Program { static void Main(string[] args) { AppDomain appDomain = AppDomain.CreateDomain("MyTemp"); appDomain.DoCallBack(loadAssembly); appDomain.DomainUnload += appDomain_DomainUnload; AppDomain.Unload(appDomain); AppDomain appDomain2 = AppDomain.CreateDomain("MyTemp2"); appDomain2.DoCallBack(loadAssembly); } private static void loadAssembly() { string fullPath = "LoadMe1.dll"; var assembly = Assembly.LoadFrom(fullPath); foreach (Type type in assembly.GetExportedTypes()) { foreach (MemberInfo memberInfo in type.GetMembers()) { string name = memberInfo.Name; Console.Out.WriteLine("name = {0}",name); } } } private static void appDomain_DomainUnload(object sender,EventArgs e) { Console.Out.WriteLine("unloaded"); } }
我没有强烈的名称组装.如果你这样做,你可能会发现第一个缓存.您可以通过从命令行运行gacutil / ldl(列表下载缓存)来判断.如果你确实发现它被缓存运行gacutil / cdl来清除下载缓存.