Je suis sûr que la cause de mon problème est question similaire:Type d'objet ne peut pas être converti en cible de type lors de la traversée AppDomains
How do I pass references as method parameters across AppDomains?
et en particulier this answer, mais je ne suis pas tout à fait sûr comment le réparer. Le problème que j'ai est ceci, j'ai une extension de Visual Studio que j'écris et l'une des choses qu'il doit pouvoir charger est de charger un assembly dans un domaine séparé (ainsi je peux le vider quand je J'en ai fini avec ça) et faire des trucs avec. J'ai donc mon domaine mis en place comme ceci:
// The actual ApplicationBase of the current domain will be the one of VS and not of my plugin
// We need our new AppDomain to be able to find our assemblies
var p = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var setup = new AppDomainSetup()
{
ApplicationBase = p
};
domain = AppDomain.CreateDomain("MyTest_AppDomain", AppDomain.CurrentDomain.Evidence, setup);
var t = typeof(Proxy);
proxy = domain.CreateInstanceAndUnwrap(t.Assembly.FullName,
t.FullName,
false,
BindingFlags.Default,
null,
new object[] { assemblyPath },
null,
null) as Proxy;
je avais besoin de régler la ApplicationBase
pour qu'il soit en mesure de charger ma classe Proxy
même si elle est définie dans le même ensemble que le code ci-dessus. Sans cela, je recevais FileNotFoundExceptions
Maintenant, ma Proxy
classe ressemble à quelque chose comme ceci:
public class Proxy : MarshalByRefObject
{
private Assembly _assembly;
private IEnumerable<Type> _types;
public IEnumerable<Type> Types
{
get
{
if (_types == null && _assembly != null)
{
_spiders = _assembly.GetTypes().Where(t => typeof(IFoo).IsAssignableFrom(t)).ToList();
}
return _types;
}
}
public Proxy(string assemblyPath)
{
_assembly = Assembly.LoadFile(assemblyPath);
}
public IFoo GetInstanceOf(string fooType)
{
var type = Types.FirstOrDefault(t => t.FullName == fooType);
if (type == null)
{
throw new InvalidOperationException($"Unknown type {fooType} in assembly {_assembly.FullName}");
}
return GetInstanceOf(type);
}
private IFoo GetInstanceOf(Type fooType)
{
var obj = Activator.CreateInstance(fooType);
return obj as IFoo;
}
public override object InitializeLifetimeService()
{
return null;
}
}
Retour dans la même classe qui a créé la nouvelle AppDomain
et l'instance de Proxy
dans le nouveau AppDomain
j'ai quelque chose comme ceci:
public IFoo GetInstanceOf(Type type)
{
var name = type.FullName;
var foo = proxy.GetInstanceOf(name);
return foo;
}
au départ, j'ai essayé d'appeler proxy.GetInstanceOf
passer directement le type, mais cela ne fonctionnait pas probablement fo r la même raison que le code que j'ai ne fonctionne pas. Quand je reçois à la ligne var foo = proxy.GetInstanceOf...
ligne, je reçois un ArgumentException
avec le message:
Type d'objet ne peut pas être converti en type de cible.
Je crois que le problème est parce que le IFoo
dans MyTest_AppDomain
n'est pas considéré comme le même IFoo
que dans DefaultDomain
, mais je ne suis pas sûr de ce que la bonne façon de résoudre ce problème est.
Il peut être pertinent que IFoo
lui-même soit défini dans un troisième ensemble distinct que mon projet VSIX et l'assemblage que j'essaie de charger dans un domaine distinct font référence. J'ai besoin de convaincre, je pense, MyTest_AppDomain
de charger le même assemblage IFoo
à partir du même emplacement que DefaultDomain
, ou bien de le convaincre qu'ils ne font qu'un.
Remarque: Toutes mes classes qui implémentent IFoo
héritent d'une base abstraite FooBase
qui elle-même hérite de MarshalByRefObject
.
Modifier Penser plus sur ce mon problème vient peut-être ici:
_assembly = Assembly.LoadFile(assemblyPath);
Le assemblyPath
il y a un chemin différent du ApplicationBase
et ce chemin a sa propre version de foo.dll
(où IFoo
est défini). Alors peut-être qu'il charge à partir de là avant le chargement de ApplicationBase
?En Proxy.GetInstanceOf
je peux regarder typeof(IFoo).Assembly.Location
et il me donne le chemin prévu qui a été placé dans ApplicationBase
, mais je ne pense pas quand mon assemblée charge foo.dll
à partir de là.
Voulez-vous que IFoo soit ajouté par référence ou prévoyez-vous une nouvelle copie dans votre domaine d'application? Vous faites déjà le premier avec MarshalByRef donc je suppose que vous voulez une nouvelle copie (mais en demandant juste au cas où). Assurez-vous que tout ce qui est implémenté par IFoo est sérialisable car l'instance doit être sérialisée et désérialisée lorsqu'elle traverse les domaines d'application. – Igor
@Igor: Je souhaite créer une instance dans le nouveau domaine de l'application et renvoyer une référence à ce domaine vers le domaine par défaut afin que je puisse appeler des méthodes et m'abonner à certains événements. –
Cela ne fonctionnera pas (par défaut). Les domaines d'application utilisent .Net remoting pour communiquer entre eux. Les instances qui sont transmises entre les domaines d'application sont essentiellement des copies. Si vous retournez 'IFoo' vous obtenez une copie de' IFoo', pas une référence à l'instance créée dans votre autre domaine d'application (par défaut) donc vous ne pouvez pas vous abonner aux événements sur IFoo ou appeler des méthodes et attendre l'instance dans votre créé un domaine d'application pour gérer ces méthodes. (plus à venir ...) – Igor