2014-07-15 18 views
7

Mam problemy z CreateInstanceAndUnwrap w tej chwili z jakiegoś powodu (pracował wcześniej).CreateInstanceAndUnwrap w innej domenie?

Mój proces to:

I dynamicznego generowania kodu i ładuje DLL z podkatalogu przez MEF. Te aplikacje następnie ładują różne części (na żądanie) z tych bibliotek DLL. Musiałem zaktualizować mój kod, aby teraz zawierał AppDomainSetup, który zawiera ścieżkę zespołu wywołującego.

Tworzę nową AppDomain poprawnie - bez problemów. Kiedy próbuję uruchomić ten kod:

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

mam ogromne problemy - runtime (zmienna powyżej) nie może rzutować na CrossDomainApplication lub ICrossDomainApplication.

Rzeczywisty obiekt wygląda tak:

public class CrossDomainApplication : MarshalByRefObject, ICrossDomainApplication 

a interfejs wygląda następująco:

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

a parametry wyglądać następująco:

[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; } 
} 

Natywna rodzaj starcie wydaje się be MarshalByRefObject - i nie lubi konwertowania do niczego innego.

Jakieś myśli o tym, co może być nie tak?

EDIT: Tutaj jest błąd pojawia się podczas uruchamiania go jak następuje:

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

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

System.InvalidCastException: Nie można rzutować przezroczyste proxy do typu „Infrastructure.ICrossDomainApplication”.

Oto co domena wygląda, jak utworzyć go:

 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 
        });    

i getPath() wygląda następująco:

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

Może być tak proste, jak CLR nie znajdujący zespołu. Nie pomiń dokumentowania * dokładnego * wyjątku, który otrzymujesz. –

+0

Wydaje mi się, że nie dostaję żadnych wyjątków - oprócz mojego idealnego przypadku powyżej, w którym po prostu rzucam go do pożądanego typu. Załączam również programy obsługi rejestrujące wywołania zwrotne zdarzeń w aplikacji AppDomain.Current, a także w nowej domenie. Nie ma żadnych wyjątków ani wywołań zwrotnych, chyba że spróbuję rzucić to szybko. – Locke

+1

Jest to bardzo częsty problem z wtyczkami. Masz ** dwie ** definicje dla ICrossDomainApplication. Pochodziły z * różnych * zespołów. Albo dlatego, że skopiowałeś zespół, albo dlatego, że dwukrotnie wprowadziłeś kod źródłowy. CLR traktuje je jako oddzielne i niekompatybilne typy. Musisz upewnić się, że dokładnie ten sam zestaw z jedyną i jedyną aplikacją ICrossDomainApplication zostanie załadowany do obu domen aplikacji. –

Odpowiedz

10

W chęci pomocy innej biednej, biednej osobie, I wreszcie wymyślił ją po wypróbowaniu TAKICH WIELU innych kombinacji.

Musiałem zmienić kilka rzeczy ...z których pierwszy:

  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 jest prawdziwy podstęp tutaj, że wszystko jeszcze włączona do (wreszcie) rozpocząć pracę. PrivateBinPath (czytaj w dokumentacji) ABSOLUTNIE musi być ścieżką względną. Jeśli masz podfolder o nazwie "złozenia", PrivateBinPath powinien być "złozeniem". Jeśli poprzedzisz ją \ lub ścieżką bezwzględną, nie zadziała.

W ten sposób spowodowało to wydarzenie AssemblyResolve pod ostatecznie pożaru. Zrobiłem to z następujących (przed utworzeniem nowego, AppDomain dziecko):

  AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly; 

oraz metodę ResolveAssembly wygląda następująco:

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; 
    } 

Metoda może być napisany znacznie łatwiej z Linq, ale trzymałem go w ten sposób, próbując uczynić to nieco oczywistym dla mnie, co było analizowane i ładowane w celu debugowania.

I, za zawsze upewnij się, że odłączyć zdarzenie (jeśli ładowanie AppDomain na raz):

  AppDomain.CurrentDomain.AssemblyResolve -= ResolveAssembly; 

To wszystko ustalone. Dziękuję wszystkim, którzy pomogli!

+2

Co to jest metoda GetPrivateBin i co robi? –

+0

To w końcu mi się udało! Dziękuję Ci! Tyle nieprzespanych nocy – shytikov

0

To nie jest odpowiedź, tylko przykładowy kod do dzielenia

Hmm, czy to może być coś innego? Ta próbka, a nie dokładnie 1: 1 z tym, co opisałeś, działa.

#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; } 
} 

Ale ja po prostu chwytam zestaw do montażu, a ty masz taki, który jest dynamicznie kompilowany. Jaki błąd i gdzie faktycznie otrzymujesz?

+0

Patrz wyżej (w celu edycji). Naprawdę nie dostaję żadnych błędów - po prostu nie mogę przekonwertować go na typ jakiego potrzebuję - w twoim przykładzie byłby to IMarshalByRefTypeInterface. Gdy "środowisko wykonawcze" było tylko obiektem typu (bez rzutowania), zalogowałem się do funkcji runtime.GetType() i zawsze chce zwrócić MarshalByRefObject ... – Locke