在运行时,我希望能够卸载DLL并重新加载它的修改版本.我的第一个实验陷入了火焰之中.谁能告诉我为什么?
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
}
Run Code Online (Sandbox Code Playgroud)
好的,这显然是我第一次发帖.感谢Daniel格式化我的代码(我现在看到工具栏按钮,以及预览窗格!).我没有办法在回复那些要求澄清原始帖子或1个答案的人时发表"评论"的方式,所以我只是发布另一个"答案"来保持对话的进行.(赞赏如何评论的指针将不胜感激).
对帖子的评论:Mitch - 陷入了火焰之中,因为我的foreach循环应该迭代修改后的DLL中的类型,而不是先前加载/卸载的类型.马修 - 这可能会奏效,但我确实需要使用相同文件名的情况.迈克二 - 没有强烈的名字.
对答案的评论:Blue和Mike Two - 我会提出你的建议,但首先我需要了解一个关键方面.我已经读过你必须注意不要将程序集拉入主应用程序域,并且代码以前在卸载之前有一个foreach循环的副本.因此,怀疑访问MethodInfos是将程序集吸入主应用程序域,我删除了循环.那时我才知道我需要寻求帮助,因为第一个DLL 仍然无法卸载!
所以我的问题是:以下代码段中的内容导致主应用程序域直接(或间接)访问dll中的任何内容 ...为什么它会导致程序集也加载到主应用程序域中:
appDomain.Load(assemblyName);
appDomain.DomainUnload += appDomain_DomainUnload;
AppDomain.Unload(appDomain);
Run Code Online (Sandbox Code Playgroud)
没有给我很大信心我在卸载之前实际上能够使用DLL.
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!
Run Code Online (Sandbox Code Playgroud)
所以我不确定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!
Run Code Online (Sandbox Code Playgroud)
那么,Mike Two,这个StackOverflow新手究竟是如何将你的答案标记为"已接受"的?或者我可以不这样做,因为我只是这里的客人?
现在我要学习如何在不将程序集吸入主应用程序域的情况下实际使用 MyLibrary.谢谢大家的参与.
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.
}
}
Run Code Online (Sandbox Code Playgroud)
似乎应该没有办法将程序集拉入这两行之间的主应用程序域:
appDomain.Load(assemblyName);
AppDomain.Unload(appDomain);
Run Code Online (Sandbox Code Playgroud)
我试图复制这个.在加载的DLL(MyLibrary.dll)中我构建了两个版本.第一个类有一个名为Foo的方法,版本号为1.0.0.0.第二个具有相同的类,但该方法已重命名为Bar(我是传统主义者),版本号为2.0.0.0.
我在卸载调用后放了一个断点.然后我尝试在第一个版本的顶部复制第二个版本.我认为这就是你正在做的事情,因为路径永远不会改变.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");
}
}
Run Code Online (Sandbox Code Playgroud)
我没有强烈的名称组装.如果你这样做,你可能会发现第一个缓存.您可以通过从命令行运行gacutil/ldl(列表下载缓存)来判断.如果你确实发现它被缓存运行gacutil/cdl来清除下载缓存.
| 归档时间: |
|
| 查看次数: |
4325 次 |
| 最近记录: |