Escribiendo Custom Actions en C# para WiX (o jugando con MSIL)

Últimamente no paro, entre eventos y proyecto de domótica no me queda mucho tiempo, pero hoy que tengo un ratio, os voy a comentar algo interesante que me he encontrado recientemente.

Como muchos sabréis, los proyectos de Setup de Visual Studio están bien para cuestiones sencillas, pero en cuanto tenemos que irnos a algo un poco más complicado, ya tenemos que usar o bien herramientas de terceros, o bien aprovecharnos de WiX para sacar toda la potencia de Windows Installer. WiX para los que no lo conozcáis, es un proyecto de SourceForge, que mediante ficheros Xml, nos permite generar paquetes de instalación de Windows Installer, a primera vista parece muy sencillo, pero la realidad es que no lo es :), además tampoco podemos decir que haya mucha y detallada documentación de WiX, aparte de un excelente tutorial, pero que está basado en la versión 2.0 (yo estoy con la 3.0).

Pues bien, el problema que se me planteaba, era que las custom actions que se pueden hacer en WiX, puesto que se ejecutan mediante Windows Installer, tienen que ser en DLLs nativas, si podemos usar un proyecto de consola manejado, para lanzar un Exe, pero perdemos gran parte de la potencia de integración, por tanto, el medio era hacer una DLL nativa, pero ups, problema, tenía que llamar a un servicio WCF, entre otras cosas, por lo que lo más “cómodo” era hacerlo en C#.

Buscando buscando, encontré un artículo en C# Corner, acerca de como exportar un método de una clase manejada, para ser usado desde código no manejado (no COM+ ojo).

La cosa es “sencilla”, aunque tendremos que modificar el MSIL, y recompilarlo, supongamos que tenemos una clase como esta:

using System;

namespace MisAccionesWiX
{
    public class MiPrimeraAccion
    {
        public static int Accion( IntPtr handle )
        {
            //Nuestro código
            return 0; //Si todo OK devolvemos 0, si queremos hacer fallar la instalación para causar un Rollback devolvemos 1
        }
    }
}

Es importante que el dato de entrada sea un IntPtr, ya que si queremos llamar a funciones de la API de Msi, como “MSiGetProperty” necesitaremos este handle.

Esto lo creamos y lo compilamos en un ensamblado de clase totalmente normal, y ahora viene la “magia”, abrimos una ventana de línea de comandos de Visual Studio, y ejecutamos, en el directorio donde tenemos la Dll compilada (suponiendo que nuestra Dll se llame MisAccionesWiX):

ildasm /OUT:MisAccionesWiX.il MisAccionesWiX.dll

con esto se nos genera el fichero MisAccionesWiX.il, que tiene el precioso código MSIL, lo primero que tenemos que decirle al ensamblado es que no sólo se va a ejecutar desde código manejado, para esto buscamos esta línea:

.corflags 0x00000001    //  ILONLY

Como véis, el comentario es bastante claro, lo único que tenemos que hacer es cambiarla por “.corflags 0x00000002”, cambiando este flag, ya le estamos diciendo que esta Dll se va a poder cargar desde un proceso no manejado (en este caso Windows Installer).

A continuación lo que vamos a hacer es exponer nuestro método para código no manejado, para esto (ver el artículo para más detalle), vamos a crear una entrada en la “v-Table fixup” del assembly, y luego crear una entrada para guardar la dirección virtual de nuestro método, lo cierto es que tampoco puedo entrar mucho en detalle aquí, ya que mis conocimientos de MSIL y CLR no llegan a tanta profundidad como me gustaría y como deberían :(.

Para hacer esto, simplemente a continuación del flag que hemos modificado agregamos estas líneas:

// Creamos la entrada en la VTable
.vtfixup [1] int32 fromunmanaged at VT_01

// Creamos la entrada para la dirección de nuestra función
.data VT_01 = int32(0)

Una vez que tengamos hecho esto, simplemente, y poco más abajo, localizamos nuestro método, en el código MSIL sería similar a esto:

.method public hidebysig static int32  Accion(native int handle) cil managed
{

….

}

Aquí, vamos a especificar que entrada VTable vamos a usar para esta función y exportar el método para código no manejado, para esto, las primeras líneas de nuestro método serán:

.method public hidebysig static int32  Accion(native int handle) cil managed
{

// Aquí damos la entrada de la VTable
.vtentry 1 : 1
//Aqui exportamos el método para código no manejado con el alias “Accion”
.export [1] as Accion

}

 

Bueno, ya casi estamos :), ahora vamos a recompilar el MSIL, para esto, y desde la línea de comandos de Visual Studio, ejecutamos:

ilasm /OUT:MisAccionesWiXNoManejado.dll MisAccionesWiX.il /DLL

Esto (si todo lo hemos hecho bien) nos generará la Dll “MisAccionesWiXNoManejado.dll” que podremos usar en nuestra Custom Action de WiX, para usarla, igual que usamos cualquier otra Dll en WiX pondremos (suponiendo que tenemos la Dll en el mismo directorio que el fichero Wxs):

<!– Referenciamos el Binario –>

<Binary Id=”MiCADll” SourceFile=”MisAccionesWiXNoManejado.dll” />

<!– Definimos la custom action –>

<CustomAction Id=”MiCAID” BinaryKey=”MiCADll” DllEntry=”Accion”/>

Y ya, en la sección de secuencia de installación, la llamamos como a cualquier otra CA.

Fácil ¿no?

Por cierto, como ya he dicho, no conozco tanto como debería y me gustaría del CLR, pero bueno, es algo que voy resolviendo poco a poco cuando tengo tiempo 🙁

Un comentario en “Escribiendo Custom Actions en C# para WiX (o jugando con MSIL)

  1. Me parecio bueno tu articulo, por eso quiero pedirte el favor de que me ayudes, tengo la coneccion a una base de datos en una dll y necesito exportarla desde otra dll todo esto en codigo c#, por referencia no me funciono.
    Muchas Gracias…..

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *