2014-07-15 10 views
7

Ich habe Probleme mit CreateInstanceAndUnwrap im Moment aus irgendeinem Grund (arbeitete vorher).CreateInstanceAndUnwrap in einer anderen Domäne?

Mein Verfahren ist folgende:

ich dynamisch einen Code generieren und es lädt DLL aus einem Unterverzeichnis über MEF. Diese Anwendungen laden dann verschiedene Teile (auf Anfrage) von diesen DLLs. Ich musste meinen Code aktualisieren, um jetzt ein AppDomainSetup einzuschließen, das den Pfad der aufrufenden Assembly enthält.

Ich erstelle die neue AppDomain korrekt - keine Probleme. Wenn ich versuche, diesen Code auszuführen:

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

Ich habe massive Probleme - die Laufzeit (variable oben) nicht mehr zu CrossDomainApplication oder ICrossDomainApplication werfen können.

Das eigentliche Objekt wie folgt aussieht:

public class CrossDomainApplication : MarshalByRefObject, ICrossDomainApplication 

Und die Schnittstelle wie folgt aussieht:

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

und die Parameter wie folgt aussehen:

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

Die native Art der Laufzeit erscheint sei MarshalByRefObject - und es mag keine Konvertierung in etwas anderes.

Irgendwelche Gedanken zu was könnte falsch sein?

EDIT: Hier ist der Fehler, den ich bekomme, wenn ich es wie folgt ausgeführt werden:

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

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

System.InvalidCastException: Kann nicht transparenten Proxy eingeben ‚Infrastructure.ICrossDomainApplication‘ werfen.

Hier ist, was die Domain aussieht, wie ich es schaffen:

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

und GetPath() sieht wie folgt aus:

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

Könnte so einfach sein, dass die CLR die Baugruppe nicht findet. Überspringe nicht die Dokumentation der exakten * Ausnahme, die du bekommst. –

+0

Es scheint keine Ausnahmen zu geben - abgesehen von meinem obigen Idealfall, wo ich es einfach auf den gewünschten Typ gewirkt habe. Ich habe auch Handler angefügt, um Ereignisrückruf auf AppDomain.Current, sowie die neue Domäne zu protokollieren. Es gibt keine Ausnahmen oder Rückrufe, es sei denn, ich versuche es schnell umzusetzen. – Locke

+1

Dies ist ein sehr häufiges Problem mit Plugins. Sie haben ** zwei ** Definitionen für ICrossDomainApplication. Sie kamen von * verschiedenen * Versammlungen. Entweder, weil Sie eine Assembly kopiert haben oder weil Sie den Quellcode zweimal eingefügt haben. Die CLR behandelt sie als separate und inkompatible Typen. Sie müssen sicherstellen, dass dieselbe Assembly mit der One-and-Only-ICrossDomainApplication in beide Anwendungsdomänen geladen wird. –

Antwort

10

In dem Wunsch zu helfen, andere arme, arme Person aus, ich schließlich fand es heraus nach dem Versuch SO VIELE andere Kombinationen.

Ich musste ein paar Dinge ändern ...von denen die erste:

  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 ist die echte Trick, die alles andere (endlich) aktiviert anfangen zu arbeiten. PrivateBinPath (lesen Sie die Dokumentation) ABSOLUT muss ein relativer Pfad sein. Wenn Sie einen Unterordner namens "Assemblies" haben, sollte der PrivateBinPath "Assemblies" sein. Wenn Sie ihm einen absoluten Pfad voranstellen, wird es nicht funktionieren.

Dadurch verursachte das Ereignis AssemblyResolve schließlich Feuer. Ich habe das mit der folgenden (vor dem neuen, Kind AppDomain erstellen):

  AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly; 

und die ResolveAssembly Methode sieht wie folgt aus:

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

Verfahren viel einfacher, mit Linq geschrieben werden kann, aber ich Ich habe es so gehalten, um es für mich selbst offensichtlich zu machen, was für Debugging-Zwecke geparst und geladen wurde.

Und je immer, stellen Sie sicher, dass Sie Ihre Veranstaltung trennen (wenn Sie eine AppDomain zu einem Zeitpunkt sind geladen):

  AppDomain.CurrentDomain.AssemblyResolve -= ResolveAssembly; 

, dass alles festgelegt. Danke an alle, die geholfen haben!

+2

Was ist die GetPrivateBin-Methode und was macht sie? –

+0

Das hat endlich den Trick für mich! Vielen Dank! So viele schlaflose Nächte – shytikov

0

Dies ist keine Antwort, Code nur Probe zu teilen

Hmm, könnte es etwas anderes sein? Dieses Sample, nicht genau 1: 1 mit dem was du beschrieben hast, funktioniert.

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

Aber dann bin ich gerade Grab Eingang Assembly, während Sie eine haben, die dynamisch kompiliert wird. Welchen Fehler und wo bekommst du eigentlich?

+0

Bitte siehe oben (für Bearbeitungen). Ich bekomme wirklich keine Fehler - ich kann es einfach nicht in den Typ konvertieren, den ich brauche - in Ihrem Beispiel wäre das IMarshalByRefTypeInterface. Wenn die "Laufzeit" nur vom Typ Objekt (kein Casting) war, loggte ich runtime.GetType() und wollte immer MarshalByRefObject zurückgeben ... – Locke

Verwandte Themen