2017-10-10 142 views
0

有许多类似的问题。但他们都已经几年了,今天不再工作了(再?)。动态加载和卸载C#程序集到应用程序域

所以这里是我的问题: 我想加载一个c#程序集到内存中,并找出它是否包含匹配我想要使用的接口的类型。如果程序集包含这个类的类型,我参考并在我的主应用程序中使用它。如果没有,我想卸载DLL,然后能够删除文件(从我的主应用程序或在应用程序运行时手动)。

我的第一个实现简单地省略了appdomain方法,但它工作(没有删除或替换程序集文件)。然后我尝试按照各种示例中所述加载程序集。但没有我能够从磁盘删除不需要的程序集文件。

要么当它们在应用程序中被引用或者我做错了某些事情时,无法卸载和删除程序集。 这就是我想在最后:

using System; 
using System.Diagnostics; 
using System.IO; 
using System.Reflection; 
using System.Runtime.CompilerServices; 
using System.Windows.Forms; 
using ESTLogViewer_V2.Formulare; 
using Parent; 
namespace ESTLogViewer_V2 
{ 
    static class Program 
    { 
     //<summary> 
     //Der Haupteinstiegspunkt für die Anwendung. 
     //</summary> 
     [STAThread] 
     static void Main() 
     { 
      LoadTest.Haupt(null); 
     } 
    } 
} 
namespace Parent 
{ 
    public class Constants 
    { 
     // adjust 
     public const string LIB_PATH = @"e:\PRJDOTNET4\ESTLogViewer V2\Plugins\LogFiles_Plugin.dll"; 
    } 

    public interface ILoader 
    { 
     string Execute(); 
    } 

    public class Loader : MarshalByRefObject, ILoader 
    { 
     public string Execute() 
     { 
      var assembly = Assembly.LoadFile(Constants.LIB_PATH); 
      foreach (var t in assembly.GetTypes()) 
      { 
       Debug.WriteLine(t.FullName); 

      } 
      return assembly.FullName; 
     } 
    } 

    class LoadTest 
    { 
     public static void Haupt(string[] args) 
     { 
      var domain = AppDomain.CreateDomain("child"); 
      var loader = (ILoader)domain.CreateInstanceAndUnwrap(typeof(Loader).Assembly.FullName, typeof(Loader).FullName); 
      Console.Out.WriteLine(loader.Execute()); 
      AppDomain.Unload(domain); 
      domain = null; 
      File.Delete(Constants.LIB_PATH); 
     } 
    } 
} 

所以,这个代码工作,直到File.Delete声明。眼下

LogFiles_Plugin.clsSchritLogDateiSplitter 
LogFiles_Plugin.clsLogFileMeldDateiSplitter 
LogFiles_Plugin.clsLogFileMeldDateiSplitterNG 
LogFiles_Plugin.clsAuftragsAnalyseSplitter 
LogFiles_Plugin.clsAuftrag 
LogFiles_Plugin.clsS_R_SignalSplitter 
LogFiles_Plugin.clsLogFileGVMeldDateiSplitter 
LogFiles_Plugin.clsLogFileGVTeleSplitter 
LogFiles_Plugin.clsLogFileGVTeleSplitter+spalte 
<PrivateImplementationDetails>{37CEBF0C-E73A-4FE7-B152-9A858E6C176D} 
<PrivateImplementationDetails>{37CEBF0C-E73A-4FE7-B152-9A858E6C176D}+__StaticArrayInitTypeSize=6256 
"ESTLogViewer V2.exe" (Verwaltet (v4.0.30319)): "C:\Windows\Microsoft.Net\assembly\GAC_MSIL\mscorlib.resources\v4.0_4.0.0.0_de_b77a5c561934e089\mscorlib.resources.dll" geladen 
Eine Ausnahme (erste Chance) des Typs "System.UnauthorizedAccessException" ist in mscorlib.dll aufgetreten. 

,一边写这个问题,我做了一些更多的测试 - 我试图找出为什么我不能删除该程序集文件:但后来它抛出一个异常。我发现,它被visual studio远程调试器锁定。

所以第二个问题是:如何避免用远程调试器锁定文件?

回答

2

我发现Assembly.LoadFile(...)会锁定文件,甚至超出卸载AppDomain的地步。

我的解决办法是先加载该程序集字节到内存中,然后将其加载到应用程序域:

var assemblyBytes = System.IO.File.ReadAllBytes("myassembly.dll"); 
var assembly = System.Reflection.Assembly.Load(assemblyBytes); 

这样的文件永不锁定,可以将其删除等

对类似问题here有更好的答案。

+0

你好@John,我用你的代码替换了我的例子中的代码,它工作。 这是“appdomain”方法的解决方案。但它应该不使用appdomain。 –

+0

@WolfgangRoth虽然它应该适用于两者,但请注意(正如[链接的答案](https://stackoverflow.com/a/18935973/3181933)所述),除非它已被加载,否则无法卸载程序集,除非它在AppDomain中。 [MSDN同意](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/assemblies-gac/how-to-load-and-unload-assemblies) – john

+0

我还没有调查装配的多重负荷的影响。但我的程序集加载的主要想法是能够实现插件到我的代码,可以由用户交换。每当用户更新插件列表时,应该添加新的插件,并且应该从显示给用户的列表中删除旧的插件 - 它可能是,应用程序将无法替换仅在实现方面不同的组件,但是其他人有相同的命名空间和名字......明天可能会试试这个。 –