Je veux charger un nouvel AppDomain
assemblage qui a un arbre de références complexe (MyDll.dll -> Microsoft.Office.Interop.Excel.dll -> Microsoft.Vbe.Interop.dll -> Office.dll -> stdole.dll)
D'après ce que j'ai compris, lorsqu'un assemblage est en cours de chargement AppDomain
, ses références ne sont pas chargées automatiquement et je dois les charger manuellement. Alors quand je fais:
string dir = @"SomePath"; // different from AppDomain.CurrentDomain.BaseDirectory
string path = System.IO.Path.Combine(dir, "MyDll.dll");
AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
setup.ApplicationBase = dir;
AppDomain domain = AppDomain.CreateDomain("SomeAppDomain", null, setup);
domain.Load(AssemblyName.GetAssemblyName(path));
et obtenu FileNotFoundException
:
Impossible de charger le fichier ou l'assembly 'MyDll, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null' ou l'une de ses dépendances. Le système ne peut pas trouver le fichier spécifié.
Je pense que l'élément clé est l' une de ses dépendances .
Ok, je fais la prochaine avant domain.Load(AssemblyName.GetAssemblyName(path));
foreach (AssemblyName refAsmName in Assembly.ReflectionOnlyLoadFrom(path).GetReferencedAssemblies())
{
domain.Load(refAsmName);
}
Mais obtenu à FileNotFoundException
nouveau, sur un autre assemblage (référencé).
Comment charger toutes les références de manière récursive?
Dois-je créer une arborescence de références avant de charger l'assembly racine? Comment obtenir les références d'un assemblage sans le charger?
la source
Réponses:
Vous devez appeler
CreateInstanceAndUnwrap
avant que votre objet proxy ne s'exécute dans le domaine d'application étranger.Notez également que si vous utilisez,
LoadFrom
vous obtiendrez probablement uneFileNotFound
exception car le résolveur d'assembly tentera de trouver l'assembly que vous chargez dans le GAC ou dans le dossier bin de l'application actuelle. Utilisez à la placeLoadFile
pour charger un fichier d'assemblage arbitraire - mais notez que si vous faites cela, vous devrez charger toutes les dépendances vous-même.la source
AppDomain.CurrentDomain.AssemblyResolve
événement comme décrit dans cette réponse MSDN . Dans mon cas, j'essayais de m'accrocher au déploiement SpecRun fonctionnant sous MSTest, mais je pense que cela s'applique à de nombreuses situations dans lesquelles votre code pourrait ne pas fonctionner à partir de l'AppDomain "principal" - extensions VS, MSTest, etc.assembly
variable référencera l'assembly de "MyDomain"? Je pense quevar assembly = value.GetAssembly(args[0]);
vous allez charger votreargs[0]
dans les deux domaines et laassembly
variable référencera la copie du domaine d'application principalhttp://support.microsoft.com/kb/837908/en-us
Version C #:
Créez une classe de modérateur et héritez-en de
MarshalByRefObject
:appel depuis le site client
la source
MarshalByRefObject
peut être passé autour des domaines d'application. Donc, je suppose que celaAssembly.LoadFrom
tente de charger l'assembly dans un nouveau domaine d'application, ce qui n'est possible que si l'objet appelant peut être passé entre ces domaines d'application. Cela s'appelle également la communication à distance comme décrit ici: msdn.microsoft.com/en-us/library/…MarshalByRefObject
ne le fait pas se charger comme par magieAppDomain
, il indique simplement au framework .NET de créer un proxy distant transparent au lieu d'utiliser la sérialisation lorsque vous décompressez la référence de l'uneAppDomain
dans l'autreAppDomain
(la méthode typique étant laCreateInstanceAndUnwrap
méthode). Je ne peux pas croire que cette réponse a plus de 30 votes positifs; le code ici juste une façon inutilement détournée d'appelerAssembly.LoadFrom
.Une fois que vous avez renvoyé l'instance d'assembly au domaine de l'appelant, le domaine de l'appelant essaiera de la charger! C'est pourquoi vous obtenez l'exception. Cela se produit dans votre dernière ligne de code:
Ainsi, tout ce que vous voulez faire avec l'assembly doit être fait dans une classe proxy - une classe qui hérite de MarshalByRefObject .
Tenez compte du fait que le domaine de l'appelant et le nouveau domaine créé doivent tous deux avoir accès à l'assembly de classe proxy. Si votre problème n'est pas trop compliqué, envisagez de laisser le dossier ApplicationBase inchangé, il sera donc identique au dossier du domaine de l'appelant (le nouveau domaine ne chargera que les assemblys dont il a besoin).
En code simple:
Si vous devez charger les assemblys à partir d'un dossier différent de votre dossier de domaine d'application actuel, créez le nouveau domaine d'application avec un dossier de chemin de recherche de DLL spécifique.
Par exemple, la ligne de création de domaine d'application du code ci-dessus doit être remplacée par:
De cette façon, toutes les dll seront automatiquement résolues à partir de dllsSearchPath.
la source
Sur votre nouveau AppDomain, essayez de définir un gestionnaire d'événements AssemblyResolve . Cet événement est appelé lorsqu'une dépendance est manquante.
la source
Vous devez gérer les événements AppDomain.AssemblyResolve ou AppDomain.ReflectionOnlyAssemblyResolve (selon la charge que vous effectuez) au cas où l'assembly référencé ne se trouve pas dans le GAC ou sur le chemin de détection du CLR.
AppDomain.AssemblyResolve
AppDomain.ReflectionOnlyAssemblyResolve
la source
Il m'a fallu un certain temps pour comprendre la réponse de @ user1996230, j'ai donc décidé de fournir un exemple plus explicite. Dans l'exemple ci-dessous, je crée un proxy pour un objet chargé dans un autre AppDomain et j'appelle une méthode sur cet objet à partir d'un autre domaine.
la source
La clé est l'événement AssemblyResolve déclenché par AppDomain.
la source
J'ai dû faire cela plusieurs fois et j'ai recherché de nombreuses solutions différentes.
La solution que je trouve la plus élégante et la plus facile à réaliser peut être mise en œuvre telle quelle.
1. Créez un projet que vous pouvez créer une interface simple
l'interface contiendra les signatures de tous les membres que vous souhaitez appeler.
Il est important de garder ce projet propre et léger. C'est un projet que les deux
AppDomain
peuvent référencer et qui nous permettra de ne pas référencer le queAssembly
nous souhaitons charger dans un domaine séparé de notre assembly client.2. Créez maintenant un projet contenant le code que vous souhaitez charger séparément
AppDomain
.Ce projet comme avec le projet client référencera le proj proxy et vous implémenterez l'interface.
3. Ensuite, dans le projet client, chargez le code dans un autre
AppDomain
.Alors, maintenant, nous créons un nouveau
AppDomain
. Peut spécifier l'emplacement de base des références d'assemblage. Le sondage vérifiera les assemblys dépendants dans GAC et dans le répertoire actuel et laAppDomain
localisation de base.si vous en avez besoin, il existe une multitude de façons différentes de charger un assemblage. Vous pouvez utiliser une méthode différente avec cette solution. Si vous avez le nom qualifié d'assembly, j'aime utiliser le
CreateInstanceAndUnwrap
car il charge les octets de l'assembly, puis instancie votre type pour vous et retourne unobject
que vous pouvez simplement convertir en votre type de proxy ou si vous ne le faites pas en code fortement typé, vous pouvez utilisez le runtime de langage dynamique et affectez l'objet retourné à unedynamic
variable typée, puis appelez simplement les membres directement dessus.Voilà.
Cela permet de charger un assembly auquel votre projet client n'a pas de référence dans un fichier séparé
AppDomain
et d'appeler les membres dessus à partir du client.Pour tester, j'aime utiliser la fenêtre Modules dans Visual Studio. Il vous montrera votre domaine d'assembly client et quels modules sont chargés dans ce domaine, ainsi que votre nouveau domaine d'application et quels assemblys ou modules sont chargés dans ce domaine.
La clé est de s'assurer que le code dérive
MarshalByRefObject
ou est sérialisable.`MarshalByRefObject vous permettra de configurer la durée de vie du domaine dans lequel il se trouve. Exemple, disons que vous voulez que le domaine soit détruit si le proxy n'a pas été appelé dans 20 minutes.
J'espère que ça aide.
la source
Foo, FooAssembly
qui a une propriété de typeBar, BarAssembly
, soit 3 assemblys au total. Cela continuerait-il de fonctionner?