c# - Compilar una versión agnostica DLL en.NET
visual-studio-2008 version (5)
Guión
Tengo dos envoltorios alrededor de Microsoft Office, uno para 2003 y otro para 2007. Dado que tener dos versiones de Microsoft Office corriendo una al lado de la otra "no es oficialmente posible" ni recomendada por Microsoft, tenemos dos cajas, una con Office 2003 y la otra con Office 2007. Compilamos los contenedores por separado. Los archivos DLL están incluidos en nuestra solución, cada cuadro tiene el mismo proceso de finalización pero con Office 2003 o 2007 "descargado" por lo que no intenta compilar ese archivo DLL en particular. Si no se realiza, se generarán errores en la compilación debido a que las DLL COM de Office no están disponibles.
Usamos .NET 2.0 y Visual Studio 2008.
Hechos
Dado que Microsoft cambió misteriosamente la API de Office 2003 en 2007, renombrando y cambiando algunos métodos ( suspiro ), por lo tanto, no son compatibles con versiones anteriores, necesitamos los dos contenedores. Tenemos cada máquina de compilación con la solución y una DLL de Office activada. Por ejemplo: la máquina con Office 2003 tiene descargada la DLL de "Office 2007", por lo tanto, no la compila. La otra caja es la misma idea pero al revés. Todo esto porque no podemos tener 2 Office diferentes en la misma caja para propósitos de programación. (Técnicamente podría tener dos Office juntas según Microsoft) pero no para programación y no sin algunos problemas.
Problema
Cuando cambiamos la versión de la aplicación (de 1.5.0.1 a 1.5.0.2 por ejemplo) necesitamos recompilar la DLL para que coincida con la nueva versión de la aplicación, esto se hace automáticamente, porque el contenedor de Office está incluido en la solución. Como las envolturas están contenidas en la solución, esas heredan la versión de la aplicación, pero tenemos que hacerlo dos veces y luego "copiar" la otra DLL a la máquina que crea el instalador. (Un dolor…)
Pregunta
¿Es posible compilar una DLL que funcionará con cualquier versión de la aplicación, a pesar de ser "anterior"? He leído algo sobre manifiestos, pero nunca tuve que interactuar con ellos. Cualquier puntero será apreciado.
La razón secreta de esto es que no hemos cambiado nuestras envolturas en "edades" y tampoco Microsoft con sus antiguas API, sin embargo, estamos recompilando la DLL para que coincida con la versión de la aplicación en cada versión que hacemos. Me gustaría automatizar este proceso en lugar de tener que depender de dos máquinas.
No puedo eliminar el archivo DLL del proyecto (ninguno de ellos) porque hay dependencias.
Podría crear un tercer "contenedor principal" pero aún no lo he pensado.
¿Algunas ideas? ¿Alguien más con el mismo requisito?
ACTUALIZAR
Aclarando:
Tengo 1 solución con N proyectos.
"Aplicación" + Office11Wrapper.dll + Office12Wrapper.dll.
Ambas "envolturas" usan dependencias para la aplicación + otras bibliotecas en la solución (capa de datos, capa ejecutiva, marco, etc.)
Cada contenedor tiene referencias para el paquete de Office respectivo (2003 y 2007).
Si compilo y no tengo Office 12 instalado, recibo errores de Office12Wrapper.dll al no encontrar las bibliotecas de Office 2007. Así que lo que tengo son dos máquinas de construcción, una con Office 2003 y otra con Office 2007. Luego de una compilación SVN update + completa en cada equipo, simplemente usamos office12.dll en el "instalador" para compilar el contenedor contra el "mismo" código, misma versión ".
Nota: Office 2007 Build Machine tiene "Wrapper for Office 2003" descargado y viceversa.
Gracias por adelantado.
Instalar Office 2003 y 2007 uno al lado del otro en la misma máquina es definitivamente posible : lo hacemos en nuestra organización incluso en estaciones de trabajo de producción para el usuario final.
En ese artículo vinculado, Microsoft recomienda que no haga esto para su uso real. Pero en su caso, parece ser solo para una sola máquina de compilación, es decir, no va a utilizar realmente ninguna versión de Office en esa máquina. En este contexto, trataré de ver si puede hacer que la instalación lado a lado funcione.
Mi suposición puede ser incorrecta, y usted está tratando de hacer esto para cada máquina de desarrollador. En ese caso, debes ignorar esta respuesta :-)
No estoy seguro de estar siguiendo completamente todo lo que dijiste, pero déjame intentarlo:
Parece que tienes una solución con 2 (?) Proyectos. Una es la aplicación real y la otra es un contenedor para la API de Office. Su aplicación tiene una referencia de proyecto a su contenedor API de Office. Nunca antes había programado para la oficina, pero parece que las API de programación son un componente común en el que solo puedes tener una versión de una máquina (es decir, 2003 o 2007, no ambas). Y tal vez aquí es donde está el problema, pero debido a que tiene una referencia de proyecto, el contenedor se compilará primero, se copiará en el directorio bin de su aplicación, donde su aplicación se vinculará a esa compilación del contenedor. Esto hará que el manifiesto de la aplicación solicite específicamente esa versión del contenedor en tiempo de ejecución.
Si tuviera el contenedor en una solución separada y añadiera una referencia a la biblioteca compilada en lugar del proyecto, siempre vincularía su aplicación a esa versión del contenedor y podría evitar el problema.
Otra opción posible es la redirección de encuadernación por ensamblaje. Esto es más avanzado y viene con su propio conjunto de problemas, pero puedes leerlo aquí .
O similar a la idea de Marc, podría extraer una interfaz y extraer algunos objetos comunes en una biblioteca de Framework, y codificar su aplicación contra la interfaz y los objetos comunes. Luego, en tiempo de ejecución use la reflexión para cargar el ensamblaje y crear una instancia del contenedor que desee.
Creo que la clave es eliminar la dependencia del proyecto si puedes. Parece que el contenedor es bastante estable y no cambia, de lo contrario, no solicitará un enlace a una versión anterior del mismo.
Solo una idea: ¿podría usar TlbExp para crear dos conjuntos de interoperabilidad (con diferentes nombres y ensamblajes) y usar una interfaz / fábrica para codificar contra los dos a través de su propia interfaz? Una vez que tenga el dll de interoperabilidad, no necesita la dependencia COM (excepto, por supuesto, para probar, etc.).
TlbImp tiene una / asmversion para la versión, por lo que podría hacerse como parte del script de compilación; pero estoy seguro de que incluso necesitas esto: solo asegúrate de que la "versión específica" sea falsa en la referencia (explorador de soluciones).
Además, sé que no ayuda, pero C # 4.0 con dynamic
y / o "No PIA" podría ayudar aquí (en el futuro, tal vez).
Cuando el programa de resolución de ensamblados .NET no puede encontrar un ensamblado al que se hace referencia en tiempo de ejecución (en este caso, no puede encontrar la versión del contenedor específico DLL con el que se vinculó la aplicación), su comportamiento predeterminado es fallar y esencialmente bloquear la aplicación. Sin embargo, este comportamiento puede anularse enganchando el evento AppDomain.AssemblyResolve. Este evento se activa cuando no se puede encontrar un ensamblado al que se hace referencia, y le da la oportunidad de sustituir otro ensamblaje en lugar del que falta (siempre que sean compatibles). Entonces, por ejemplo, podría sustituir una versión anterior de la DLL contenedora que cargue usted mismo.
La mejor manera que he encontrado para hacer esto es agregar un constructor estático en la clase principal de la aplicación que engancha el evento, por ejemplo:
using System.Reflection;
static Program()
{
AppDomain.CurrentDomain.AssemblyResolve += delegate(object sender, ResolveEventArgs e)
{
AssemblyName requestedName = new AssemblyName(e.Name);
if (requestedName.Name == "Office11Wrapper")
{
// Put code here to load whatever version of the assembly you actually have
return Assembly.LoadFile("Office11Wrapper.DLL");
}
else
{
return null;
}
}
}
Al poner esto en un constructor estático de la clase de aplicación principal, se garantiza que se ejecutará antes de que cualquier código intente acceder a cualquier elemento en la DLL del contenedor, asegurándose de que el enlace esté en su lugar con anticipación.
También puede usar archivos de política para redireccionar la versión, pero eso tiende a ser más complejo.
¡Un buen trabajo de exploración! Acabo de lanzar una implementación basada en el concepto presentado anteriormente, y funciona maravillosamente:
static Assembly domain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string partialName = args.Name.Substring(0, args.Name.IndexOf('',''));
return Assembly.Load(new AssemblyName(partialName));
}
Por supuesto, hay margen para la mejora, ¡pero este me sirve!