2012-11-19 3 views
5

스택 오버플로뿐만 아니라 3 가지 다른 Google 검색의 첫 페이지에있는 모든 파란색 링크와 마찬가지로 많은 다른 버전의이 질문을 읽었습니다. 자습서뿐만 아니라 MSDN (어셈블리 실행 이상으로 얕은)입니다. 나는 Tao가 좋은 테스트 케이스로 작동하도록하기위한 나의 노력만을 생각할 수 있지만, 나는 단순한 문자열 리턴, double, 매개 변수가있는 함수로 시도했다. 내 문제가 무엇이든간에 그것은 타오가 아닙니다.DLL의 동적로드/언로드 Redux (물론 AppDomain 사용)

기본적으로 GLPlugin 네임 스페이스에 내 Draw 클래스의 testLibraryDomain.CreateInstance()을 만들고 싶습니다.

namespace GLPlugin 
{ 
    public class DrawingControl : MarshalByRefObject 
    { 
     public DrawingControl() 
     { 
      Gl.glColor3f(1.0f , 0.0f , 0.0f); 

      //this is a test to make sure it passes 
      //to the GL Rendering context... success 
     } 
    } 
} 

실제로 펜 색상을 변경 :

 if(usePlugin) 
     { 
       AppDomain testLibraryDomain = AppDomain.CreateDomain("TestGLDomain2"); 

       //What the heck goes here so that I can simply call 
       //the default constructor and maybe a function or two? 

       AppDomain.Unload(testLibraryDomain); 
     } 
     Gl.glBegin(Gl.GL_TRIANGLES); 

내가 있다는 사실을 알고있다. 내가 static void Main(string args[]) 진입 점을 주었을 때 작동하며 testLibraryDomain.ExecuteAssembly(thePluginFilePath)라고합니다. GL 호출이 AppDomain의 "최상위 수준"의 OpenGL 컨텍스트에 맞는지 확신 할 수 없기 때문에 직접 ExecuteAssembly가 작동할지 여부를 염려했습니다. 심지어 어셈블리를 덮어 쓰고 펜 색상을 두 번째로 변경할 수도 있습니다. 불행하게도 실행 가능한 진입 점을 제공한다는 것은 팝업 콘솔이 나를 방해하고 나간다는 것을 의미합니다. 또한 단순히 프로젝트에서 참조를 제공하고 일반 GLPlugin.DrawingTool tool = new GLPlugin.DrawingControl()을 만들거나 someAssembly = Assembly.LoadFrom(thePluginFilePath)을 만들 때도 작동합니다 (물론 불행하게도 조립품을 잠그고 교체/재 컴파일을 방지합니다).

내가 시도한 다양한 방법 중 하나를 사용할 때 "주어진 어셈블리 이름이나 코드베이스가 유효하지 않습니다."라는 메시지가 항상 나타납니다. 나는 그것이 유효하다는 것을 약속한다. 내가 그것을로드하려고하는 방식으로 뭔가가 아닙니다. 내가 부족 해요 알고

한 가지 내가 말할 수있는의 AssemblyName 인수가 어셈블리 파일에 파일 경로 아니므로 지금까지 testLibraryDomain.CreateInstance(string assemblyName , string typeName);

에 대한 올바른 설정입니다. 네임 스페이스 또는 어셈블리 이름일까요? 예 : GLPlugin? 그렇다면 실제 파일을 어디에서 참조 할 수 있습니까? someAppDomain.LoadFrom (someFilename)은 없지만, 편리하다면 someDomain.LoadFrom (someFilename)은 없습니다. 또한 Type은 무엇이며 문자열 typeName은 무엇입니까? 나는 객체의 인스턴스가 아닌 다른 타입을 생성하지 않기 때문에 여기에 "Object"을 넣고 싶지 않습니다. 나는 또한 AppDomain의 기본적인 이해가 부족한 CreateInstanceAndUnwrap(... , ...)을 시도했습니다. 보통 내가 왜 "왜?"를 이해하지 못하더라도 자습서를 통해 혼란스러워하고 일할 수 있습니다. 보통 6 개의 다른 튜토리얼을 찾아 보는 것이 도움이됩니다. 여기에서 다시는 아니지만 모든 사람이 근본적으로 (또는 그렇다고 보입니다) 접근하기 때문에 도움이됩니다.

제발 ELI5 ... 별도의 AppDomain에있는 DLL에서 클래스 인스턴스를로드하고, 몇 가지 함수를 실행하고 언로드하려고합니다. 결국 목록으로 이러한 함수의 목록을 만들 필요에 따라 제거/업데이트 ... 나는 그들에게도 인수를 전달할 수 있기를 원하지만 2 단계가 될 것입니다. StackOverflow에 따르면, 나는 serializable에 대해 배워야 만합니다. 나는 다른 날을 연기 할 것이다. (내가하는 일을 내 사례에서 파악할 수있을 것이라고 상상해보십시오.)

답변

11

좋아, 우리는 몇 가지를 분명히해야합니다. 로드하고 iteslf을 파일을 잠그지 않고 다른 응용 프로그램 도메인에 DLL을 언로드 할 수 있도록하려면 첫째, 어쩌면이 같은 방법을 사용할 수 있습니다

AppDomain apd = AppDomain.CreateDomain("newdomain"); 
using(var fs = new FileStream("myDll.dll", FileMode.Open)) 
{ 
    var bytes = new byte[fs.Length]; 
    fs.Read(bytes, 0, bytes .Length); 
    Assembly loadedAssembly = apd.Load(bytes); 
} 

이 방법을, 파일 잠금 및되지 않습니다 나중에 도메인을 언로드하고 파일을 다시 컴파일하고 나중에 최신 버전으로로드 할 수 있어야합니다. 그러나 이것이 귀하의 응용 프로그램을 깰 수없는 경우 100 % 확실하지 않습니다.

두 번째 이유 때문입니다. MSDN에 따라 CreateInstanceAndUnwrap 메서드를 사용하는 경우 두 appdomains (호출중인 호출과 호출중인 호출)에 어셈블리를로드해야합니다. 그리고 AppDomains에로드 된 두 개의 다른 dll이있을 때 상황이 끝날 수 있습니다.

The assembly that contains unwrapped class must be loaded into both application domains, but it can load other assemblies that exist only in the new application domain.

지금은 기억하지 않는다,하지만 난 당신이 CreateInstanceAndUnwrap를 호출 할 때 두 응용 프로그램 도메인에있는 객체 생성의 동작이 다를 수 있다고 생각하지만, 나는 세부 사항을 기억하지 않습니다.

플러그인 아키텍처의 경우이 블로그 게시물을 읽을 수 있습니다. About how to handle Dynamic Plugins using the AppDomain Class to Load and Unload Code

나는이 작품을 응용 프로그램 도메인과 내가 약간의 혼동을 소개하는 방법을 잊어

편집 할 수 있습니다. 나는 '플러그인'아키텍처가 어떻게 작동 할 수 있는지에 대한 간단한 예를 준비했다. 그것은 이전에 넣어 둔 블로그에서 설명한 것과 매우 흡사합니다. 여기에 Shadow Copying을 사용하는 샘플이 있습니다. 어떤 이유로 든 사용하지 않으려한다면 사용하기 쉽게 변경할 수 있습니다. AppDomain.Load(byte[] bytes)

3 개의 어셈블리가 있습니다. 첫 번째 어셈블리는 기본 플러그인 어셈블리이며, 프록시로 작동하며로드됩니다. 모든 AppDomains (우리의 경우 - 기본 앱 도메인 및 플러그인 앱 도메인에 있음).

여기서 우리는 SamplePlugin.dll이라는 더미 플러그인으로 어셈블리를 만들고 "Plugins"폴더에 저장합니다. 그것은 PluginBaseLib.dll가

그림자 복사가 매우 편리 할 것으로 보인다

namespace SamplePlugin 
{ 
    public class MySamplePlugin : MyPluginBase 
    { 
     public MySamplePlugin() 
     { } 

     public override void DrawingControl() 
     { 
      var color = Console.ForegroundColor; 
      Console.ForegroundColor = ConsoleColor.Green; 
      Console.WriteLine("----------------------"); 
      Console.WriteLine("This was called from app domian {0}", AppDomain.CurrentDomain.FriendlyName); 
      Console.WriteLine("I have following assamblies loaded:"); 
      foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) 
      { 
       Console.WriteLine("\t{0}", assembly.GetName().Name); 
      } 
      Console.WriteLine("----------------------"); 
      Console.ForegroundColor = color; 
     } 
    } 
} 

만 PluginBaseLib.dll를 참조합니다 마지막 조립 (간단한 콘솔 응용 프로그램) 및

namespace ConsoleApplication1 
{ 
    //'Default implementation' which doesn't use any plugins. In this sample 
    //it just lists the assemblies loaded in AppDomain and AppDomain name itself. 
    public static void DrawControlsDefault() 
    { 
     Console.WriteLine("----------------------"); 
     Console.WriteLine("No custom plugin, default app domain {0}", AppDomain.CurrentDomain.FriendlyName); 
     Console.WriteLine("I have following assamblies loaded:"); 
     foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) 
     { 
      Console.WriteLine("\t{0}", assembly.GetName().Name); 
     } 
     Console.WriteLine("----------------------"); 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      //Showing that we don't have any additional plugins loaded in app domain. 
      DrawControlsDefault(); 

      var appDir = AppDomain.CurrentDomain.BaseDirectory; 
      //We have to create AppDomain setup for shadow copying 
      var appDomainSetup = new AppDomainSetup 
           { 
            ApplicationName = "", //with MSDN: If the ApplicationName property is not set, the CachePath property is ignored and the download cache is used. No exception is thrown. 
            ShadowCopyFiles = "true",//Enabling ShadowCopy - yes, it's string value 
            ApplicationBase = Path.Combine(appDir,"Plugins"),//Base path for new app domain - our plugins folder 
            CachePath = "VSSCache"//Path, where we want to have our copied dlls store. 
           }; 
     var apd = AppDomain.CreateDomain("My new app domain", null, appDomainSetup); 

     //Loading dlls in new appdomain - when using shadow copying it can be skipped, 
     //in CreatePlugin method all required assemblies will be loaded internaly, 
     //Im using this just to show how method can be called in another app domain. 
     //but it has it limits - method cannot return any values and take any parameters. 

     //apd.DoCallBack(new CrossAppDomainDelegate(MyPluginsHelper.LoadMyPlugins)); 

     //We are creating our plugin proxy/factory which will exist in another app domain 
     //and will create for us objects and return their remote 'copies'. 
     var proxy = (MyPluginFactory) apd.CreateInstance("PluginBaseLib", "PluginBaseLib.MyPluginFactory").Unwrap(); 

     //if we would use here method (MyPluginBase) apd.CreateInstance("SamplePlugin", "SamplePlugin.MySamplePlugin").Unwrap(); 
     //we would have to load "SamplePlugin.dll" into our app domain. We may not want that, to not waste memory for example 
     //with loading endless number of types. 
     var instance = proxy.CreatePlugin("SamplePlugin", "SamplePlugin.MySamplePlugin"); 
     instance.DrawingControl(); 

     Console.WriteLine("Now we can recompile our SamplePlugin dll, replace it in Plugin directory and load in another AppDomain. Click Enter when you ready"); 
     Console.ReadKey(); 

     var apd2 = AppDomain.CreateDomain("My second domain", null, appDomainSetup); 
     var proxy2 = (MyPluginFactory)apd2.CreateInstance("PluginBaseLib", "PluginBaseLib.MyPluginFactory").Unwrap(); 
     var instance2 = proxy2.CreatePlugin("SamplePlugin", "SamplePlugin.MySamplePlugin"); 
     instance2.DrawingControl(); 

     //Now we want to prove, that this additional assembly was not loaded to prmiary app domain. 
     DrawControlsDefault(); 

     //And that we still have the old assembly loaded in previous AppDomain. 
     instance.DrawingControl(); 

     //App domain is unloaded so, we will get exception if we try to call any of this object method. 
     AppDomain.Unload(apd); 
     try 
     { 
      instance.DrawingControl(); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex); 
     } 

     Console.ReadKey(); 
    } 
} 

}를 참조있다.

+1

잠금없이 어셈블리를로드하려면 섀도 복사본을 사용하는 것이 더 좋습니다 (http://msdn.microsoft.com/en-us/library/ms404279.aspx). – Maarten

+0

나는 당신의 예제에 대해서 궁금해한다. 기본 지식이 부족하기 때문이다 ... 새로운'loadedAssembly'를 디폴트 최상위 레벨이 아닌'apd' AppDomain에 "적용"할 것인가? AppDomain을 생성 한 것처럼 순서대로 암시되어 있습니까? 그러면 Unload()가 실행될 때까지 그 아래에있는 모든 것이 AppDomain의 일부가됩니다. 또한, 훌륭한 기사, 내 머리 위로 조금. 나는 그것의 일부를 찢어 버리고 광고 사이트에 올려 놓았으나 전체 기사를 갖는 것이 좋다. 나는 오늘부터 그 과정을 통해 정말로 배우려고 노력할 것입니다. 그것이 기능적인 소스 파일을 가지기를 바란다. – Adam

+0

귀하의 질문을 이해하는 경우 모르겠지만, 나는 예전의 예전 응용 프로그램에서 몇 가지 예와 의견을 가지고 놀아 보려고합니다. –