2014-07-15 1 views
7

나는 (이전에 작동하고 있던) 어떤 이유로 인해 CreateInstanceAndUnwrap에 문제가 있습니다.다른 도메인에서 CreateInstanceAndUnwrap을 만드시겠습니까?

내가 동적으로 몇 가지 코드를 생성하며 MEF를 통해 하위 디렉토리에서 DLL의로드 :

내 프로세스는이입니다. 그런 다음 이러한 응용 프로그램은 해당 DLL에서 다른 요구 사항을로드합니다. 호출 어셈블리의 경로가 포함 된 AppDomainSetup을 포함하도록 코드를 업데이트해야했습니다.

새로운 AppDomain을 올바르게 만듭니다. 문제는 없습니다. 나는이 코드를 실행하려고하면 :

object runtime = domain.CreateInstanceAndUnwrap(
       typeof(CrossDomainApplication).Assembly.FullName, 
       typeof(CrossDomainApplication).FullName); 

을 나는 거대한 문제가 없다 - 런타임 (위의 변수가) 더 이상 CrossDomainApplication 또는 ICrossDomainApplication으로 캐스팅 할 수 있습니다.

public class CrossDomainApplication : MarshalByRefObject, ICrossDomainApplication 

그리고 인터페이스는 다음과 같습니다 :

public interface ICrossDomainApplication 
{ 
    void Run(CrossDomainApplicationParameters parameters); 
} 

그리고 매개 변수의 모양 :

[Serializable] 
public class CrossDomainApplicationParameters : MarshalByRefObject 
{ 
    public object FactoryType { get; set; } 
    public Type ApplicationType { get; set; } 
    public string ModuleName { get; set; } 
    public object[] Parameters { get; set; } 
} 

런타임의 기본 형태가 나타납니다

실제 목적은 같다 MarshalByRefObject가되어야하며 다른 것으로 변환하는 것을 좋아하지 않습니다.

무엇이 잘못 될 수 있는지에 대한 의견이 있으십니까?

편집 :

  ICrossDomainApplication runtime = (ICrossDomainApplication)domain.CreateInstanceAndUnwrap(
        typeof(CrossDomainApplication).Assembly.FullName, 
        typeof(CrossDomainApplication).FullName); 

      //Exception before reaching here 
      runtime.Run(parameters); 

System.InvalidCastException : 'Infrastructure.ICrossDomainApplication'을 입력 할 투명 프록시를 캐스팅 할 수 없습니다를 여기에 내가 다음으로 실행할 때 내가 오류입니다. 여기

도메인 내가 그것을 만들 때, 어떻게 표시되는지를 보여줍니다 :

 AppDomain domain = AppDomain.CreateDomain(
        Guid.NewGuid().ToString(), 
        null, 
        new AppDomainSetup() 
        { 
         ApplicationBase = GetPath(), 
         ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile, 
         ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName, 
         LoaderOptimization = LoaderOptimization.MultiDomainHost 
        });    

및 GetPath()는 다음과 같습니다

private string GetPath() 
    { 
     Uri path = new Uri(Assembly.GetCallingAssembly().CodeBase); 

     if (path.IsFile) 
     { 
      path = new Uri(path, Path.GetDirectoryName(path.AbsolutePath)); 
     } 

     return path.LocalPath.Replace("%20", " "); 
    } 
+1

어셈블리를 찾지 못하는 CLR만큼 간단 할 수 있습니다. * 정확한 * 예외를 문서화하는 것을 건너 뛰지 마십시오. –

+0

예외를 얻는 것 같지 않습니다. 이상적인 경우를 제외하고는 원하는 유형으로 캐스트했습니다. 또한 새로운 도메인뿐만 아니라 AppDomain.Current에서 이벤트 콜백을 기록하기 위해 핸들러를 첨부했습니다. 내가 그것을 빨리 던지려고하지 않는다면 어떤 예외 나 콜백도 없다. – Locke

+1

이것은 플러그인에서 자주 발생하는 문제입니다. ICrossDomainApplication에 대해 ** 두 가지 ** 정의가 있습니다. 그들은 다른 어셈블리에서 왔습니다. 어셈블리를 복사했거나 소스 코드를 두 번 포함했기 때문입니다. CLR에서는이를 별도의 호환되지 않는 형식으로 처리합니다. 하나의 ICrossDomainApplication을 가진 정확히 동일한 어셈블리가 두 appdomain에 모두로드되도록해야합니다. –

답변

10

다른 가난하고 가난한 사람을 도우려는 욕망에서 나는 마침내 다른 조합을 시도한 후에 그것을 알아 냈습니다.

몇 가지 사항을 변경해야했습니다 ...처음있는 :

  AppDomain domain = AppDomain.CreateDomain(
        Guid.NewGuid().ToString(), 
        AppDomain.CurrentDomain.Evidence, 
        new AppDomainSetup() 
        { 
         ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase, 
         ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile, 
         LoaderOptimization = LoaderOptimization.MultiDomainHost, 
         PrivateBinPath = GetPrivateBin(AppDomain.CurrentDomain.SetupInformation.ApplicationBase) 
        }); 

PrivateBinPath가 다른 모든 것을 가능하게 여기 실제 트릭 (마지막으로) 작업을 시작합니다. PrivateBinPath (문서 읽기) 절대적으로 상대 경로 여야합니다. "assemblies"라는 하위 폴더가있는 경우 PrivateBinPath는 "assembly"이어야합니다. \ 또는 절대 경로를 앞에두면 작동하지 않습니다.

이렇게하면 AssemblyResolve 이벤트가 으로 끝나고 결국이 발생합니다.

  AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly; 

과 ResolveAssembly 방법 다음과 같습니다 : 나는 다음에 (새로운, 자식 응용 프로그램 도메인을 만들기 전에이) 있다고했다

private Assembly ResolveAssembly(object sender, ResolveEventArgs args) 
    { 
     var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); 

     foreach (var assembly in loadedAssemblies) 
     { 
      if (assembly.FullName == args.Name) 
      { 
       return assembly; 
      } 
     } 

     return null; 
    } 
방법은 Linq에로 훨씬 쉽게 쓸 수

,하지만 난 디버깅 목적으로 파싱되고로드되는 것을 나 자신에게 다소 분명하게하려고이 방법을 사용했다.

그리고, (당신이 시간에 응용 프로그램 도메인을로드하는 경우) 항상 당, 당신이 당신의 이벤트를 분리해야합니다 모든 것을 고정

  AppDomain.CurrentDomain.AssemblyResolve -= ResolveAssembly; 

. 도와 주신 모든 분들께 감사드립니다!

+2

GetPrivateBin 메서드 란 무엇이며 어떤 기능을합니까? –

+0

그 마침내 나를 위해 트릭을 했어! 고맙습니다! 이렇게 많은 불면의 밤 – shytikov

0

이없는 답변, 그냥 샘플 코드를 공유하는 것입니다

흠, 뭔가 다른 것일 수 있습니까? 당신이 묘사 한 것과 정확히 1 : 1이 아닌이 샘플은 작동합니다.

#region 

using System; 
using System.Reflection; 
using System.Threading; 

#endregion 

internal class Program 
{ 
    #region Methods 

    private static void Main(string[] args) 
    { 
     // Get and display the friendly name of the default AppDomain. 
     string callingDomainName = Thread.GetDomain().FriendlyName; 
     Console.WriteLine(callingDomainName); 

     // Get and display the full name of the EXE assembly. 
     string exeAssembly = Assembly.GetEntryAssembly().FullName; 
     Console.WriteLine(exeAssembly); 

     // Construct and initialize settings for a second AppDomain. 
     var ads = new AppDomainSetup 
        { 
         ApplicationBase = Environment.CurrentDirectory, 
         DisallowBindingRedirects = false, 
         DisallowCodeDownload = true, 
         ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile 
        }; 

     // Create the second AppDomain. 
     AppDomain ad2 = AppDomain.CreateDomain("AD #2", null, ads); 

     // Create an instance of MarshalbyRefType in the second AppDomain. 
     // A proxy to the object is returned. 
     var mbrt = (MarshalByRefType)ad2.CreateInstanceAndUnwrap(exeAssembly, typeof(MarshalByRefType).FullName); 

     // Call a method on the object via the proxy, passing the 
     // default AppDomain's friendly name in as a parameter. 
     mbrt.SomeMethod(new MarshalByRefParameter{ModuleName = callingDomainName}); 

     // Unload the second AppDomain. This deletes its object and 
     // invalidates the proxy object. 
     AppDomain.Unload(ad2); 
     try 
     { 
      // Call the method again. Note that this time it fails 
      // because the second AppDomain was unloaded. 
      mbrt.SomeMethod(new MarshalByRefParameter { ModuleName = callingDomainName }); 
      Console.WriteLine("Successful call."); 
     } 
     catch (AppDomainUnloadedException) 
     { 
      Console.WriteLine("Failed call; this is expected."); 
     } 
    } 


    #endregion 
} 

public interface IMarshalByRefTypeInterface 
{ 
    void SomeMethod(MarshalByRefParameter parameter); 
} 

public class MarshalByRefType : MarshalByRefObject, IMarshalByRefTypeInterface 
{ 
    // Call this method via a proxy. 

    #region Public Methods and Operators 

    public void SomeMethod(MarshalByRefParameter parameter) 
    { 
     // Get this AppDomain's settings and display some of them. 
     AppDomainSetup ads = AppDomain.CurrentDomain.SetupInformation; 
     Console.WriteLine("AppName={0}, AppBase={1}, ConfigFile={2}", ads.ApplicationName, ads.ApplicationBase, ads.ConfigurationFile); 

     // Display the name of the calling AppDomain and the name 
     // of the second domain. 
     // NOTE: The application's thread has transitioned between 
     // AppDomains. 
     Console.WriteLine("Calling from '{0}' to '{1}'.", parameter.ModuleName, Thread.GetDomain().FriendlyName); 
    } 

    #endregion 
} 

[Serializable] 
public class MarshalByRefParameter : MarshalByRefObject 
{ 
    public string ModuleName { get; set; } 
} 

그렇다면 동적으로 컴파일 된 항목을 가져 오는 동안 입력 어셈블리를 잡는 중입니다. 어떤 오류가 있으며 어디서 실제로 얻습니까?

+0

위 참조 (수정 사항 참조). 나는 오류를 실제로 얻지는 않는다. - 나는 꼭 필요한 타입으로 변환 할 수 없다 - 당신의 예제에서 IMarshalByRefTypeInterface가 될 것이다. "런타임"이 객체 유형 (캐스팅 없음) 일 때 runtime.GetType()을 로깅했으며 항상 MarshalByRefObject를 반환하려고합니다 ... – Locke