简体   繁体   English

我的应用域名不会卸载

[英]My app domain won't unload

At runtime, I'd like to be able to unload a DLL and reload a modified version of it. 在运行时,我希望能够卸载DLL并重新加载它的修改版本。 My first experiment went down in flames. 我的第一个实验陷入了火焰之中。 Can anyone tell me why? 谁能告诉我为什么?

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
}

Edit: 编辑:

Okay, this is obviously my first time posting. 好的,这显然是我第一次发帖。 Thanks Daniel for formatting my code (I now see the toolbar button to do that, and the preview pane, too!). 感谢Daniel格式化我的代码(我现在看到工具栏按钮,以及预览窗格!)。 I don't see a way to post a "comment" in reply to either those who asked for clarification to the original post or to the 1 answer, so I'll just post another "answer" to keep the conversation going. 我没有办法在回复那些要求澄清原始帖子或1个答案的人时发表“评论”的方式,所以我只是发布另一个“答案”来保持对话的进行。 (Pointers appreciated on how comments are done would be appreciated). (赞赏如何评论的指针将不胜感激)。

Comments to the posting: Mitch - Went down in flames 'cause my foreach loop should have been iterating over the types in the modified DLL, not the previously loaded/unloaded one. 对帖子的评论:Mitch - 陷入了火焰之中,因为我的foreach循环应该迭代修改后的DLL中的类型,而不是先前加载/卸载的类型。 Matthew - That might work, but I really need the case of the same file name to work. 马修 - 这可能会奏效,但我确实需要使用相同文件名的情况。 Mike Two - Not strongly named. 迈克二 - 没有强烈的名字。

Comments to the answer: Blue & Mike Two - I'll noodle on your suggestions, but first I need to understand a critical aspect. 对答案的评论:Blue和Mike Two - 我会提出你的建议,但首先我需要了解一个关键方面。 I had read that you have to take care not to pull the assembly into the main app domain, and the code previously had a copy of the foreach loop before the unload. 我已经读过你必须注意不要将程序集拉入主应用程序域,并且代码以前在卸载之前有一个foreach循环的副本。 So, suspecting that accessing MethodInfos was sucking the assembly into the main app domain, I removed the loop. 因此,怀疑访问MethodInfos是将程序集吸入主应用程序域,我删除了循环。 That's when I knew I needed to ask for help, 'cause the first DLL still wouldn't unload! 那时我才知道我需要寻求帮助,因为第一个DLL 仍然无法卸载!

So my question is: What in the following code segment causes the main app domain to ever directly (or indirectly) access anything in the dll...why would it cause the assembly to also load in the main app domain: 所以我的问题是:以下代码段中的内容导致主应用程序域直接(或间接)访问dll中的任何内容 ...为什么它会导致程序集也加载到主应用程序域中:

appDomain.Load(assemblyName);
appDomain.DomainUnload += appDomain_DomainUnload;
AppDomain.Unload(appDomain);

Didn't give me much confidence I'd ever actually be able to use the DLL before unloading it. 没有给我很大信心我在卸载之前实际上能够使用DLL。


Edit 2: 编辑2:

Mike Two - Thanks for your persistence...loading the assembly from within DoCallBack was the secret sauce. Mike Two - 感谢您的坚持......从DoCallBack中加载程序集是秘诀。 For anyone interested, here is some more info that might be useful down the road: 对于任何感兴趣的人,这里有一些可能有用的信息:

Sounds like no one could actually reproduce my conditions exactly. 听起来没有人能真正重现我的条件。 To demonstrate the original problem, I generated my dlls this way: 1. Added class library project to solution 2. Built version 1.0.0 with Foo; 为了演示原始问题,我以这种方式生成了我的dll:1。将类库项目添加到解决方案2.使用Foo构建版本1.0.0; renamed resulting assembly as MyLibrary.dll.f. 将生成的程序集重命名为MyLibrary.dll.f。 3. Renamed Foo to Goo and built another version 1.0.0; 3.将Foo重命名为Goo并构建另一个版本1.0.0; renamed resulting assembly as MyLibrary.dll.g. 将生成的程序集重命名为MyLibrary.dll.g。 4. Removed project from solution. 4.从解决方案中删除项目。 Before starting the run, I removed the .f and ran to breakpoint (first line after the unload). 在开始运行之前,我删除了.f并运行到断点(卸载后的第一行)。 Then I tacked the .f back on and removed the .g from the other dll and ran to the next breakpoint. 然后我重新打开.f并从另一个dll中移除.g并运行到下一个断点。 Windows didn't stop me from renaming. Windows并没有阻止我重命名。 Note: Although it would be better practice, I didn't change the version number because I didn't want to assume my customers always would, since the default entry in AssemblyInfo isn't the wildcard version. 注意:虽然这是更好的练习,但我没有更改版本号,因为我不想假设我的客户总是这样,因为AssemblyInfo中的默认条目不是通配符版本。 It seemed like the uglier case to handle. 这似乎是一个丑陋的案件。

Also, I just now discovered something that would have clued me in sooner: 此外,我刚刚发现了一些可以早日让我了解的东西:

 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!

So I'm not sure what the point of AppDomain.Load is, but it seems to have the bonus side effect of loading the assembly into the main app domain as well. 所以我不确定AppDomain.Load是什么意思,但它似乎也有加载程序集到主应用程序域的额外副作用。 Using this experiment, I could see Mike Two's solution cleanly loads it only into the temp domain: 使用这个实验,我可以看到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!

So, Mike Two, exactly how does this StackOverflow newbie mark your answer as "accepted"? 那么,Mike Two,这个StackOverflow新手究竟是如何将你的答案标记为“已接受”的? Or can I not do that since I'm only a guest here? 或者我可以不这样做,因为我只是这里的客人?

Now I'm off to learn how to actually use MyLibrary without sucking the assembly into the main app domain. 现在我要学习如何在不将程序集吸入主应用程序域的情况下实际使用 MyLibrary。 Thanks everyone for participating. 谢谢大家的参与。


Edit 3: 编辑3:

BlueMonkMN - I went ahead and took out the event subscription and get the same result. BlueMonkMN - 我继续前进并取消了活动订阅并得到了相同的结果。 The full program is now listed below: 完整的程序现在列在下面:

 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.
    }
 }

Seems like there should be no way the assembly is getting pulled into the main app domain between these 2 lines: 似乎应该没有办法将程序集拉入这两行之间的主应用程序域:

appDomain.Load(assemblyName); 
AppDomain.Unload(appDomain);

I tried to replicate this. 我试图复制这个。 In the dll to load (MyLibrary.dll) I built two versions. 在加载的DLL(MyLibrary.dll)中我构建了两个版本。 The first had one class with one method named Foo and had a version number of 1.0.0.0. 第一个类有一个名为Foo的方法,版本号为1.0.0.0。 The second had the same class but the method had been renamed Bar (I'm a traditionalist) and a version number of 2.0.0.0. 第二个具有相同的类,但该方法已重命名为Bar(我是传统主义者),版本号为2.0.0.0。

I put a breakpoint after the unload call. 我在卸载调用后放了一个断点。 Then I tried to copy the second version on top of the first version. 然后我尝试在第一个版本的顶部复制第二个版本。 I assume that is what you are doing because the path never changes. 我认为这就是你正在做的事情,因为路径永远不会改变。 Windows would not let me copy version 2 over version 1. The dll was locked. Windows不允许我在版本1上复制版本2.dll被锁定。

I changed the code to load the dll using code executed inside the AppDomain by using DoCallback. 我使用DoCallback更改了代码以使用在AppDomain中执行的代码加载dll。 That worked. 那很有效。 I could swap the dll's and find the new method. 我可以交换dll并找到新方法。 Here is the code. 这是代码。

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");
    }
}

I did not strong name the assemblies. 我没有强烈的名称组装。 If you do you are likely to find the first one cached. 如果你这样做,你可能会发现第一个缓存。 You can tell by running gacutil /ldl (List download cache) from the command line. 您可以通过从命令行运行gacutil / ldl(列表下载缓存)来判断。 If you do find it cached run gacutil /cdl to clear the download cache. 如果你确实发现它被缓存运行gacutil / cdl来清除下载缓存。

I have found that if you ever directly access types in an assembly it gets loaded into your own domain. 我发现如果您直接访问程序集中的类型,它将被加载到您自己的域中。 So what I have had to do is create a third assembly that implements interfaces common to both assemblies. 所以我必须做的是创建第三个程序集,实现两个程序集共有的接口。 That assembly gets loaded into both domains. 该程序集被加载到两个域中。 Then just be careful to only use interfaces from that third assembly when interacting with the external assembly. 然后,在与外部组件交互时,请注意仅使用第三个组件的接口。 That should allow you to unload the second assembly by unloading its domain. 这应该允许您通过卸载其域来卸载第二个程序集。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 从App域卸载程序集 - Unload Assembly From App Domain 将C#类库加载到其自己的应用程序域中,并在发生异常时卸载而不会干扰第三方应用程序 - Load my C# class library in its own app domain and unload when exception occurs without disturbing third party application 我想在单独的应用程序域中动态卸载程序集,但它不起作用 - I want to dynamically unload an assembly in a separate app domain and it does not work 我试图卸载应用程序域,但是我的应用程序停止工作 - I tried to unload an Application domain but my application stopped working 为什么我的SMS应用程序不写入SQL Server数据库? - Why won't my SMS app write to a SQL Server database? 为什么我的控制台应用程序不能使用25个或更多参数? - Why won't my console app work with 25 arguments or more? 为什么我的C#应用​​程序不能在不同的机器上执行? - Why won't my C# app execute on a different machine? 为什么我的循环不会在 Windows 窗体应用程序中继续 - why won't my loop continue in windows forms app 卸载并重新安装mono后,我的winforms应用程序无法启动 - After uninstalling and reinstalling mono my winforms app won't start 卸载默认应用程序域的事件? - Unload event for the default Application Domain?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM