2011-01-21 4 views
3

런타임에 어셈블리를 컴파일하고로드하는 방법을 찾으려고합니다. 기본 의도는 디스크가 아닌 데이터베이스에 저장하는 것입니다. 그래서 몇 가지 코드를 작성했지만 흥미로운 상황을 보았습니다.AppDomain.Load()에 대한 흥미로운 오류

//SumLib 
namespace SumLib 
{ 
    public class SumClass 
    { 
     public static int Sum(int a, int b) 
     { 
      return a + b; 
     } 
    } 
} 


// Console app 
class Program 
{ 

    public static void AssemblyLoadEvent(object sender, AssemblyLoadEventArgs args) 
    { 

     object[] tt = { 3, 6 }; 
     Type typ = args.LoadedAssembly.GetType("SumLib.SumClass"); 
     MethodInfo minfo = typ.GetMethod("Sum"); 
     int x = (int)minfo.Invoke(null, tt); 
     Console.WriteLine(x); 
    } 

    static void Main(string[] args) 
    { 

     AppDomain apd = AppDomain.CreateDomain("newdomain", AppDomain.CurrentDomain.Evidence, AppDomain.CurrentDomain.SetupInformation); 
     apd.AssemblyLoad += new AssemblyLoadEventHandler(AssemblyLoadEvent); 

     FileStream fs = new FileStream("Sumlib.dll", FileMode.Open); 
     byte[] asbyte = new byte[fs.Length]; 
     fs.Read(asbyte, 0, asbyte.Length); 
     fs.Close(); 
     fs.Dispose(); 

//  File.Delete("Sumlib.dll"); 

     apd.Load(asbyte); 

     Console.ReadLine(); 
    } 
} 

코드는 라인이 AssemblyLoadEvent() 방법은 내가 콘솔에 숫자 9를 참조, 실행, 내가 주석을 제거하면 응용 프로그램 도메인이 어셈블리를로드 주석 삭제 완벽하게 실행 : 여기 내 코드입니다 , 메서드가 끝나면 apd.Load()에서 "파일 또는 어셈블리를로드 할 수 없습니다."라는 오류가 발생합니다. 그것은 아주 합리적입니다.

질문 : 디스크에 어셈블리 파일없이 AssemblyLoadEvent() 메서드를 실행할 수있는 방법은 무엇입니까?

어떻게 든 원시 이진 데이터의 도움으로 실행되는 경우 appdomain이 Load() 메서드를 성공적으로 완료하는 방법이 있습니까?

+0

바이트 []에서 어셈블리를로드하려고합니다. 그게 맞습니까? –

+0

@ 샘 B : 네 맞습니다. –

답변

2

따라서 바이트 []에서 어셈블리를로드하고 메소드를 호출하려고합니다. 모든 종속성에 대해 호출 될 것이므로 AssemblyLoad 이벤트로 작업 한 방식을 권장하지 않습니다.

@Jester는 부모 도메인의 Load()를 사용하여 어셈블리를로드하는 것이 옳습니다. 이 문제를 해결하려면 다음과 같은 래퍼 클래스를 사용하는 것이 좋습니다.

// Console app 
class Program 
{ 
    public class AssemblyLoader : MarshalByRefObject 
    { 
     public void LoadAndCall(byte[] binary) 
     { 
      Assembly loadedAssembly = AppDomain.CurrentDomain.Load(binary); 
      object[] tt = { 3, 6 }; 
      Type typ = loadedAssembly.GetType("SumLib.SumClass"); 
      MethodInfo minfo = typ.GetMethod("Sum", BindingFlags.Static | BindingFlags.Public); 
      int x = (int)minfo.Invoke(null, tt); 
      Console.WriteLine(x); 
     } 
    } 

    static void Main() 
    { 
     AppDomain apd = AppDomain.CreateDomain("newdomain", AppDomain.CurrentDomain.Evidence, AppDomain.CurrentDomain.SetupInformation); 
     FileStream fs = new FileStream("Sumlib.dll", FileMode.Open); 
     byte[] asbyte = new byte[fs.Length]; 
     fs.Read(asbyte, 0, asbyte.Length); 
     fs.Close(); 
     fs.Dispose(); 
     File.Delete("Sumlib.dll");  

     AssemblyLoader loader = (AssemblyLoader)apd.CreateInstanceAndUnwrap(typeof(AssemblyLoader).Assembly.FullName, typeof(AssemblyLoader).FullName); 
     loader.LoadAndCall(asbyte); 
     Console.ReadLine(); 
     } 
} 
+0

흥미 진진한 hmmm. 따라서 appdomain을 언로드하면 어셈블리를 메모리에서 제거 할 수 있습니까? –

+0

@sad_man : 예 –

+0

@sad_man : 리플렉션으로 대상 어셈블리를 호출 중이므로 LoadAndCall 내에 try-catch 블록을 삽입하여 반사 문제를 감지하는 것이 좋습니다. –

6

어셈블리를 "newdomain"에로드하고 이벤트 처리기 을 아직 newdomain에 호출합니다 (이벤트 처리기에서 현재 도메인을 인쇄하는 경우이를 확인할 수 있음). 마지막으로 전달 된 반환 값을 만듭니다. 샘플 코드에서 해당 반환 값을 무시하지만 여전히 만들어집니다. deserialization이 어셈블리를 기본 도메인에도로드하려고하기 때문에 예외는 교차 도메인 마샬링 중에 발생합니다. 여기

모노에서 예외 호출 스택입니다 :

at System.AppDomain.Load (System.String assemblyString, System.Security.Policy.Evidence assemblySecurity, Boolean refonly) [0x00000] in <filename unknown>:0 
    at System.AppDomain.Load (System.String assemblyString) [0x00000] in <filename unknown>:0 
    at (wrapper remoting-invoke-with-check) System.AppDomain:Load (string) 
    at System.Reflection.Assembly.Load (System.String assemblyString) [0x00000] in <filename unknown>:0 
    at System.UnitySerializationHolder.GetRealObject (StreamingContext context) [0x00000] in <filename unknown>:0 
    at System.Runtime.Serialization.ObjectRecord.LoadData (System.Runtime.Serialization.ObjectManager manager, ISurrogateSelector selector, StreamingContext context) [0x00000] in <filename unknown>:0 
    at System.Runtime.Serialization.ObjectManager.DoFixups() [0x00000] in <filename unknown>:0 
    at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadNextObject (System.IO.BinaryReader reader) [0x00000] in <filename unknown>:0 
    at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObjectGraph (BinaryElement elem, System.IO.BinaryReader reader, Boolean readHeaders, System.Object& result, System.Runtime.Remoting.Messaging.Header[]& headers) [0x00000] in <filename unknown>:0 
    at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.NoCheckDeserialize (System.IO.Stream serializationStream, System.Runtime.Remoting.Messaging.HeaderHandler handler) [0x00000] in <filename unknown>:0 
    at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream) [0x00000] in <filename unknown>:0 
    at System.Runtime.Remoting.RemotingServices.DeserializeCallData (System.Byte[] array) [0x00000] in <filename unknown>:0 
    at (wrapper xdomain-invoke) System.AppDomain:Load (byte[]) 
    at (wrapper remoting-invoke-with-check) System.AppDomain:Load (byte[]) 
    at Program.Main (System.String[] args) [0x00000] in <filename unknown>:0 

편집 :

현재 응용 프로그램이 아닙니다 대상 응용 프로그램 도메인에로드를 호출하는 시도 : 여기 MSDN의 확인입니다 도메인을 사용하면 대상 응용 프로그램 도메인에서 어셈블리가 성공적으로로드됩니다. 어셈블리가 MarshalByRefObject가 아니므로이 메서드가로드 된 어셈블리의 어셈블리를 현재 응용 프로그램 도메인으로 반환하려고하면 공용 언어 런타임에서 어셈블리를 현재 응용 프로그램 도메인에로드하려고 시도하고로드가 실패 할 수 있습니다. 현재 응용 프로그램 도메인에로드 된 어셈블리는 두 응용 프로그램 도메인의 경로 설정이 다른 경우 먼저로드 된 어셈블리와 다를 수 있습니다.

0

Shadow Copy 매개 변수를 사용하지 않는 이유는 무엇입니까? 도움이 될지도 모릅니다.

+0

주로 shodow가 복사 된 assemlies가 ApplicationBase에서 지정한 응용 프로그램 디렉토리 또는 하위 디렉토리에 저장되어 있어야하기 때문입니다. 이 방법으로 어셈블리를 데이터베이스에 저장하고 필요한 경우로드 할 수도 있습니다. –