2012-01-22 11 views
2

Dans mon défaut (confiance totale) AppDomain Je veux créer un bac à sable AppDomain et abonnez-vous à un événement dans ce:Pourquoi ne puis-je pas m'abonner à un événement dans un AppDomain de confiance partielle?

class Domain : MarshalByRefObject 
{ 
    public event Action TestEvent; 
} 

Domain domain = AppDomainStarter.Start<Domain>(@"C:\Temp", "Domain", null, true); 
domain.TestEvent +=() => { }; // SecurityException 

Abonnement échoue avec le message « Demande d'autorisation de type « System.Security. Permissions.ReflectionPermission, mscorlib, Version = 4.0.0.0 ... 'a échoué. "

(Pour la définition de AppDomainStarter, voir my answer to another question.)

Notez que le ApplicationBaseC: \ Temp est pas le dossier qui contient l'assembly qui contient du domaine. C'est délibéré; mon but est de charger un deuxième assembly non approuvé tiers dans le nouveau AppDomain, et ce second assembly est situé dans C: \ Temp (ou ailleurs, peut-être même un partage réseau). Mais avant de pouvoir charger le second assemblage, j'ai besoin de charger ma classe Domain dans le nouveau AppDomain. Cela réussit, mais pour une raison quelconque, je ne peux pas m'abonner à un événement à travers la limite AppDomain (je peux appeler des méthodes, mais pas m'abonner à des événements). : De toute évidence, lors de l'abonnement à un événement dans un sandbox AppDomain, la méthode de l'abonné et la classe qui contient l'abonné doivent être publiques. Par exemple:

public static class Program 
{ 
    class Domain : MarshalByRefObject 
    { 
     public event Action TestEvent; 
     public Domain() { Console.WriteLine("Domain created OK"); } 
    } 
    static void Main() 
    { 
     string loc = @"C:\Temp"; 
     Domain domain = AppDomainStarter.Start<Domain>(loc, "Domain", null, true); 
     // DIFFERENT EXCEPTION THIS TIME! 
     domain.TestEvent += new Action(domain_TestEvent); 
    } 
    public static void domain_TestEvent() { } 
} 

Cependant, je ne peux toujours pas m'abonner à l'événement. La nouvelle erreur est "Impossible de charger le fichier ou l'assemblage 'TestApp, Version = 1.0.0.0, Culture = Neutre, PublicKeyToken = null' ou l'une de ses dépendances.Le système ne trouve pas le fichier spécifié." En un sens, cela a du sens parce que j'ai spécifié le "mauvais" dossier "C: \ Temp" comme ApplicationBase de mon nouvel AppDomain, mais d'une certaine manière cela n'a aucun sens parce que l'assemblage "TestApp" est déjà chargé dans les deux AppDomains. Comment est-il possible que le CLR ne trouve pas un assembly déjà chargé?

De plus, il ne fait aucune différence si j'ajoute l'autorisation d'accéder au dossier qui contient mon assemblage:

string folderOfT = Path.GetFullPath(Path.Combine(typeof(T).Assembly.Location, "..")); 
permSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read, folderOfT)); 
// Same exception still occurs 

Je peux « régler » le problème en utilisant une valeur différente pour AppDomainSetup.ApplicationBase:

string loc = Path.GetFullPath(Assembly.GetExecutingAssembly().Location + @"\.."); 

Cela élimine l'exception, mais je ne peux pas utiliser cette "solution" car le but de l'AppDomain est de charger un assembly non approuvé à partir d'un dossier différent du dossier qui contient mon propre assembly. Par conséquent, loc doit être le dossier qui contient l'assembly non approuvé, pas celui qui contient mon assembly.

Répondre

0

L'exception provient du domaine d'approbation partielle et non de l'approbation complète. Vous devez avoir omis d'accorder ReflectionPermission dans le domaine d'approbation partielle

+0

(i) Je n'essaie pas de faire de réflexion, (ii) ReflectionPermission accorde l'accès pour accéder à des membres privés de tout type accessible; IL EST AFIN qu'il n'est pas nécessaire d'avoir accès aux membres du public. Je ne veux pas accorder ReflectionPermission car cela pourrait ouvrir des failles de sécurité. – Qwertie

+0

(i) comme vous l'avez vu, ce n'est pas forcément votre code mais l'infrastructure qui doit le faire (c'est pourquoi rendre le type public résolu ce problème) ii) assez juste et vous avez contourné ce problème de toute façon –

+0

le dossier "plug in" un sous-dossier de l'appbase existant? alors vous n'avez pas besoin de changer l'appbase de votre domaine d'application et l'assembly sera chargé correctement. Vous pouvez toujours contraindre le fichier IO dans le répertoire plug-in via une FileIOPermission –

0

Les assemblys sont résolus sur une base par AppDomain. Puisque vous démarrez le nouveau AppDomain dans un répertoire différent (et que votre assembly n'est pas enregistré dans le GAC), il ne peut pas localiser l'assembly.Vous pouvez modifier votre code AppDomainStarter d'abord charger l'ensemble ciblé, puis créer une instance de cette assemblée:

  Assembly assembly = Assembly.LoadFrom(typeof(T).Assembly.ManifestModule.FullyQualifiedName); 
     return (T)assembly.CreateInstance(typeof(T).FullName, false, 0, null, constructorArgs, null, null); 
+0

Le code que vous avez posté chargera l'assembly dans le même AppDomain que l'appelant, et non dans un nouvel AppDomain, et de plus l'assembly en question est ** déjà chargé ** (T est Program.Domain dans ce cas, et Program .Main() est déjà sur la pile des appels!). – Qwertie

0

La seule chose que je pouvais trouver qui fonctionne est de mettre l'ensemble (qui contient le code que vous voulez exécuter dans un nouvel AppDomain) dans le GAC. Bien sûr, c'est une énorme douleur dans la crosse, mais c'est la seule chose qui fonctionne.

Ci-dessous, je vais décrire quelques choses que j'ai essayé qui ne marchait pas.

Dans certaines circonstances, Visual Studio 2010 vous donnera ce message lorsque vous appelez Activator.CreateInstanceFrom (je ne sais pas quand exactement - une application console propre ne produit pas):

Géré assistant Debugging 'LoadFromContext' a détecté un problème dans 'C: \ Users ... \ TestApp.vshost.exe'. Informations supplémentaires: L'assembly nommé 'TestApp' a été chargé à partir de 'file: /// C: /Users/.../TestApp.exe' en utilisant le contexte LoadFrom. L'utilisation de ce contexte peut entraîner un comportement inattendu pour la sérialisation, la diffusion et la résolution des dépendances. Dans la plupart des cas, il est recommandé d'éviter le contexte LoadFrom. Cela peut être effectué par en installant des assemblys dans le Global Assembly Cache ou dans le répertoire ApplicationBase et en utilisant Assembly.Load lors du chargement explicite des assemblys .

La documentation de Assembly.LoadFrom comprend cette déclaration: « Si un ensemble est chargé avec LoadFrom, et plus tard un ensemble dans le contexte de charge tente de charger [la] même ensemble par nom d'affichage, la tentative échoue de charge Cela peut. se produire lorsqu'un assembly est désérialisé. " Malheureusement, il n'y a aucune indication sur pourquoi cela se produit.

Dans le code exemple, une assemblée n'est pas désérialisée (et je ne suis pas tout à fait sûr de ce que cela signifie pour désérialiser une assemblée en premier lieu), mais un délégué est désérialisée; il est raisonnable de supposer que la désérialisation d'un délégué implique une tentative de chargement du même assemblage "par nom d'affichage". Si cela était vrai, il ne serait pas possible de passer un délégué à travers les limites AppDomain si le délégué pointe vers une fonction qui se trouve dans un assembly qui a été chargé en utilisant le "contexte LoadFrom". Dans ce cas, l'utilisation CreateInstance au lieu de CreateInstanceFrom pourrait éviter ce problème (car CreateInstanceFrom utilise LoadFrom):

return (T)Activator.CreateInstance(newDomain, 
    typeof(T).Assembly.FullName, 
    typeof(T).FullName, false, 
    0, null, constructorArgs, null, null).Unwrap(); 

Mais cela se révèle être un hareng rouge; CreateInstance ne peut pas être utilisé à moins que le ApplicationBase est réglé sur le dossier qui contient notre assemblée, et si ApplicationBase est réglé sur ce dossier, puis inscrivez-vous à TestEvent réussit peu importe si CreateInstance ou CreateInstanceFrom a été utilisé pour créer T dans le nouveau AppDomain. Par conséquent, le fait que T ait été chargé via LoadFrom ne provoque pas le problème tout seul.

Une autre chose que j'ai essayé était de signer l'assemblage et dire le.NET Framework qu'il doit être donné FullTrust:

newDomain = AppDomain.CreateDomain(appDomainName, null, setup, permSet, 
    new StrongName[] { GetStrongName(typeof(T).Assembly) }); 

Cela repose sur la méthode GetStrongName de an MSDN article. Malheureusement, cela n'a aucun effet (c'est-à-dire que l'exception FileNotFoundException se produit toujours).

Questions connexes