2017-10-10 84 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

我還沒有調查裝配的多重負荷的影響。但我的程序集加載的主要想法是能夠實現插件到我的代碼,可以由用戶交換。每當用戶更新插件列表時,應該添加新的插件,並且應該從顯示給用戶的列表中刪除舊的插件 - 它可能是,應用程序將無法替換僅在實現方面不同的組件,但是其他人有相同的命名空間和名字......明天可能會試試這個。 –