Quiero cargar en un nuevo AppDomain
ensamblado que tiene un árbol de referencias complejo (MyDll.dll -> Microsoft.Office.Interop.Excel.dll -> Microsoft.Vbe.Interop.dll -> Office.dll -> stdole.dll)
Por lo que tengo entendido, cuando se carga un ensamblaje AppDomain
, sus referencias no se cargan automáticamente y tengo que cargarlas manualmente. Entonces, cuando lo hago:
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));
y obtuve FileNotFoundException
:
No se pudo cargar el archivo o ensamblado 'MyDll, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null' o una de sus dependencias. El sistema no puede encontrar el archivo especificado.
Creo que la parte clave es una de sus dependencias. .
Ok, hago lo siguiente antes domain.Load(AssemblyName.GetAssemblyName(path));
foreach (AssemblyName refAsmName in Assembly.ReflectionOnlyLoadFrom(path).GetReferencedAssemblies())
{
domain.Load(refAsmName);
}
Pero tengo FileNotFoundException
nuevo, en otro ensamblado (referenciado).
¿Cómo cargar todas las referencias de forma recursiva?
¿Tengo que crear un árbol de referencias antes de cargar el ensamblaje raíz? ¿Cómo obtener las referencias de un ensamblado sin cargarlo?
fuente
Respuestas:
Debe invocar
CreateInstanceAndUnwrap
antes de que su objeto proxy se ejecute en el dominio de la aplicación externa.Además, tenga en cuenta que si lo usa
LoadFrom
, probablemente obtendrá unaFileNotFound
excepción porque el solucionador de ensamblados intentará encontrar el ensamblado que está cargando en el GAC o en la carpeta bin de la aplicación actual. ÚseloLoadFile
para cargar un archivo de ensamblaje arbitrario en su lugar, pero tenga en cuenta que si hace esto, deberá cargar las dependencias usted mismo.fuente
AppDomain.CurrentDomain.AssemblyResolve
evento como se describe en esta respuesta de MSDN . En mi caso, estaba tratando de conectarme a la implementación de SpecRun que se ejecuta bajo MSTest, pero creo que se aplica a muchas situaciones en las que su código podría no ejecutarse desde el AppDomain "primario" - Extensiones VS, MSTest, etc.assembly
variable hará referencia al ensamblado de "MyDomain"? Creovar assembly = value.GetAssembly(args[0]);
que cargará suargs[0]
en ambos dominios y laassembly
variable hará referencia a la copia del dominio de la aplicación principalhttp://support.microsoft.com/kb/837908/en-us
Versión C #:
Cree una clase de moderador y heredela de
MarshalByRefObject
:llamar desde el sitio del cliente
fuente
MarshalByRefObject
puede pasar por los dominios de aplicación. Entonces, supongo queAssembly.LoadFrom
intenta cargar el ensamblado en un nuevo dominio de aplicación, lo que solo es posible, si el objeto de llamada pudiera pasarse entre esos dominios de aplicación. Esto también se denomina comunicación remota como se describe aquí: msdn.microsoft.com/en-us/library/…MarshalByRefObject
no hace que se cargue mágicamente en todos los demásAppDomain
, solo le dice al marco .NET que cree un proxy remoto transparente en lugar de usar la serialización cuando desenvuelve la referencia de unoAppDomain
en otroAppDomain
(la forma típica es elCreateInstanceAndUnwrap
método). No puedo creer que esta respuesta tenga más de 30 votos a favor; el código aquí es solo una forma indirecta de llamar sin sentidoAssembly.LoadFrom
.Una vez que vuelva a pasar la instancia de ensamblado al dominio de la persona que llama, el dominio de la persona que llama intentará cargarla. Es por eso que obtiene la excepción. Esto sucede en su última línea de código:
Por lo tanto, lo que sea que desee hacer con el ensamblado, debe hacerlo en una clase proxy, una clase que hereda MarshalByRefObject .
Tenga en cuenta que el dominio de la persona que llama y el nuevo dominio creado deben tener acceso al ensamblaje de la clase de proxy. Si su problema no es demasiado complicado, considere dejar la carpeta ApplicationBase sin cambios, por lo que será la misma que la carpeta del dominio de la persona que llama (el nuevo dominio solo cargará los ensamblados que necesita).
En código simple:
Si necesita cargar los ensamblados desde una carpeta que es diferente a la carpeta del dominio de la aplicación actual, cree el nuevo dominio de la aplicación con la carpeta de ruta de búsqueda de dlls específica.
Por ejemplo, la línea de creación del dominio de la aplicación del código anterior debe reemplazarse con:
De esta forma, todas las DLL se resolverán automáticamente desde dllsSearchPath.
fuente
En su nuevo AppDomain, intente configurar un controlador de eventos AssemblyResolve . Ese evento se llama cuando falta una dependencia.
fuente
Debe manejar los eventos AppDomain.AssemblyResolve o AppDomain.ReflectionOnlyAssemblyResolve (según la carga que esté realizando) en caso de que el ensamblado al que se hace referencia no esté en el GAC o en la ruta de prueba de CLR.
AppDomain.AssemblyResolve
AppDomain.ReflectionOnlyAssemblyResolve
fuente
Me tomó un tiempo entender la respuesta de @ user1996230, así que decidí proporcionar un ejemplo más explícito. En el siguiente ejemplo, hago un proxy para un objeto cargado en otro AppDomain y llamo a un método en ese objeto desde otro dominio.
fuente
La clave es el evento AssemblyResolve generado por AppDomain.
fuente
He tenido que hacer esto varias veces y he investigado muchas soluciones diferentes.
La solución que encuentro más elegante y fácil de lograr se puede implementar como tal.
1. Crea un proyecto que puedas crear con una interfaz simple
la interfaz contendrá las firmas de los miembros a los que desee llamar.
Es importante mantener este proyecto limpio y ligero. Es un proyecto al que ambos
AppDomain
pueden hacer referencia y nos permitirá no hacer referencia alAssembly
que deseamos cargar en un dominio separado de nuestro ensamblaje de cliente.2. Ahora cree un proyecto que tenga el código que desea cargar por separado
AppDomain
.Este proyecto, al igual que el proyecto de cliente, hará referencia al proyecto de proxy y usted implementará la interfaz.
3. A continuación, en el proyecto del cliente, cargue el código en otro
AppDomain
.Entonces, ahora creamos un nuevo
AppDomain
. Puede especificar la ubicación base para referencias de ensamblaje. El sondeo buscará ensamblados dependientes en GAC y en el directorio actual y laAppDomain
base loc.si es necesario, hay un montón de formas diferentes de cargar un ensamblaje. Puede utilizar esta solución de una forma diferente. Si tiene el nombre calificado de ensamblado, entonces me gusta usar el,
CreateInstanceAndUnwrap
ya que carga los bytes de ensamblaje y luego crea una instancia de su tipo para usted y devuelve unobject
que puede convertir simplemente a su tipo de proxy o, si no, en un código fuertemente tipado, podría use el tiempo de ejecución del lenguaje dinámico y asigne el objeto devuelto a unadynamic
variable escrita y luego simplemente llame a los miembros directamente.Ahí tienes.
Esto permite cargar un ensamblado al que el proyecto de su cliente no tiene referencia en un
AppDomain
y llamar a los miembros desde el cliente.Para probar, me gusta usar la ventana Módulos en Visual Studio. Le mostrará el dominio de ensamblado de su cliente y todos los módulos que se cargan en ese dominio, así como su nuevo dominio de aplicación y qué ensamblajes o módulos se cargan en ese dominio.
La clave es asegurarse de codificar o derivar
MarshalByRefObject
o serializable.`MarshalByRefObject le permitirá configurar la vida útil del dominio en el que se encuentra. Por ejemplo, digamos que desea que el dominio se destruya si no se ha llamado al proxy en 20 minutos.
Espero que esto ayude.
fuente
Foo, FooAssembly
que tenga una propiedad de tipoBar, BarAssembly
, es decir, 3 ensamblados en total. ¿Continuaría funcionando?