etiquetas - metaetiqueta wix
Trucos y consejos de WiX (30)
Creando versiones en vivo, de prueba, de entrenamiento, ... usando los mismos archivos de origen.
En pocas palabras: cree un código de actualización único para cada instalador y defina automáticamente el primer carácter de cada Guid para cada instalador, dejando los 31 restantes restantes.
Prerrequisitos
Suposiciones
- Las variables de WiX se utilizan para definir UpgradeCode, ProductName, InstallName.
- Ya tienes un instalador en funcionamiento. No intentaría esto hasta que lo hagas.
- Todos sus componentes se guardan en un archivo (Components.wxs). Este proceso funcionará si tiene varios archivos, solo habrá más trabajo por hacer.
Estructura de directorios
- Setup.Library
- Todos los archivos wxs (Componentes, Características, Diálogos de UI, ...)
- Common.Config.wxi (ProductCode = "*", ProductVersion, PlatformProgramFilesFolder, ...)
- Setup.Live (wixproj)
- Enlace todos los archivos de Setup.Library usando "Agregar archivo existente" -> "Agregar como enlace" (el pequeño botón de flecha hacia abajo justo al lado del botón Agregar en Visual Studio)
- Config.wxi (tiene un código de actualización único, nombre de producto, nombre de instalación, ...)
- Setup.Test , ...
- como en vivo, pero Config.wxi está configurado para el entorno de prueba.
Proceso
- Cree el directorio Setup.Library y mueva todos sus archivos wxs y wxi (excepto Config.wxi) del proyecto existente.
- Cree Setup.Live, Setup.Test, etc. de acuerdo con wixproj normal.
- Agregue BeforeBuild target en wixproj en Setup.Live, etc. para realizar la Actualización de archivos de la tarea de la comunidad de MSBuild para modificar las Guías (utilicé A para Live, B para Test y C para entrenamiento)
- Agregue AfterBuild target para revertir las guides de Components.wxs a 0.
- Verifique con Orca que cada componente de cada MSI tenga el guid modificado.
- Verifique que las guías originales estén restauradas.
- Verifique que cada MSI esté instalando (y actualizando) el producto y la ubicación correctos.
Ejemplo Config.wxi
<?xml version="1.0" encoding="utf-8"?>
<Include>
<!-- Upgrade code should not change unless you want to install
a new product and have the old product remain installed,
that is, both products existing as separate instances. -->
<?define UpgradeCode = "YOUR-GUID-HERE" ?>
<!-- Platform specific variables -->
<?if $(var.Platform) = x64 ?>
<!-- Product name as you want it to appear in Add/Remove Programs-->
<?define ProductName = "Foo 64 Bit [Live]" ?>
<?else ?>
<?define ProductName = "Foo [Live]" ?>
<?endif ?>
<!-- Directory name used as default installation location -->
<?define InstallName = "Foo [Live]" ?>
<!-- Registry key name used to store installation location -->
<?define InstallNameKey = "FooLive" ?>
<?define VDirName = "FooLive" ?>
<?define AppPoolName = "FooLiveAppPool" ?>
<?define DbName = "BlahBlahLive" ?>
</Include>
Ejemplo Config.Common.wxi
<?xml version="1.0" encoding="utf-8"?>
<Include>
<!-- Auto-generate ProductCode for each build, release and upgrade -->
<?define ProductCode = "*" ?>
<!-- Note that 4th version (Revision) is ignored by Windows Installer -->
<?define ProductVersion = "1.0.0.0" ?>
<!-- Minimum version supported if product already installed and this is an upgrade -->
<!-- Note that 4th version (Revision) is ignored by Windows Installer -->
<?define MinimumUpgradeVersion = "0.0.0.0" ?>
<!-- Platform specific variables -->
<?if $(var.Platform) = x64 ?>
<?define Win64 = "yes" ?>
<?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
<?else ?>
<?define Win64 = "no" ?>
<?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
<?endif ?>
<?define ProductManufacturer = "Foo Technologies"?>
<!-- Decimal Language ID (LCID) for the Product. Used for localization. -->
<?define ProductLanguage = "1033" ?>
<?define WebSiteName = "DefaultWebSite" ?>
<?define WebSitePort = "80" ?>
<?define DbServer = "(local)" ?>
</Include>
Ejemplo Components.wxs
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<!-- The pre-processor variable which allows the magic to happen :) -->
<?include $(sys.CURRENTDIR)/Config.wxi?>
<?include ../Setup.Library/Config.Common.wxi?>
<Fragment Id="ComponentsFragment">
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="$(var.PlatformProgramFilesFolder)">
<Directory Id="INSTALLLOCATION" Name="$(var.InstallName)">
<Component Id="ProductComponent" Guid="0XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" KeyPath="yes">
...
Nota: ahora sugeriría dejar el atributo Guid fuera del componente (equivalente a *
), usar un archivo por componente y establecer el archivo como la ruta de acceso clave. Esto elimina la necesidad de llamar a los ModifyComponentsGuids
y RevertComponentsGuids
que se muestran a continuación. Sin embargo, esto podría no ser posible para todos sus componentes.
Ejemplo Setup.Live.wixproj
<Import Project="$(MSBuildExtensionsPath)/MSBuildCommunityTasks/MSBuild.Community.Tasks.Targets" />
<Target Name="BeforeBuild">
<CallTarget Targets="ModifyComponentsGuids" />
</Target>
<Target Name="AfterBuild">
<CallTarget Targets="RevertComponentsGuids" />
</Target>
<!-- Modify the first character of every Guid to create unique value for Live, Test and Training builds -->
<Target Name="ModifyComponentsGuids">
<FileUpdate Files="../Setup.Library/Components.wxs" Regex="Guid="([a-f]|[A-F]|/d)" ReplacementText="Guid="A" />
</Target>
<!-- Revert the first character of every Guid back to initial value -->
<Target Name="RevertComponentsGuids">
<FileUpdate Files="../Setup.Library/Components.wxs" Regex="Guid="([a-f]|[A-F]|/d)" ReplacementText="Guid="0" />
</Target>
Pensamientos finales
- Este proceso también debería funcionar para crear diferentes instaladores para diferentes módulos de fusión (en vivo, prueba, ... como características) para el mismo instalador. Fui con diferentes instaladores, ya que parecía una opción más segura, hay más riesgo de que alguien actualice Live en lugar de Training si está en la misma caja y solo usa las funciones para los diferentes módulos de combinación.
- Si usa su MSI para realizar actualizaciones, así como nuevas instalaciones, es decir, el enfoque de solo actualización principal y guarda su ubicación de instalación en el registro, recuerde crear una variable para el nombre de clave para cada instalación.
- También creamos variables en cada Config.wxi para habilitar nombres únicos de directorios virtuales, grupos de aplicaciones, nombres de bases de datos, etc. para cada instalador.
ACTUALIZACIÓN 1: Guids de componentes de generación automática elimina la necesidad de llamar a la tarea FileUpdate si crea un componente con Guid = "*" para cada archivo, estableciendo el archivo como ruta de acceso clave.
ACTUALIZACIÓN 2: Uno de los problemas con los que nos hemos encontrado es que si no genera automáticamente las Guías de componentes y la compilación falla, los archivos temporales deben eliminarse manualmente.
ACTUALIZACIÓN 3: Encontré una manera de eliminar la dependencia de svn: externals y la creación de archivos temporales. Esto hace que el proceso de construcción sea más resistente (y es la mejor opción si no puede usar comodines en sus Guías) y menos frágil si hay una falla en la construcción en la luz o la vela.
ACTUALIZACIÓN 4: el soporte para múltiples instancias que usan transformaciones de instancia está en WiX 3.0+, definitivamente también vale la pena verlo.
Hemos estado usando WiX por un tiempo y, a pesar de las quejas habituales sobre la facilidad de uso, está funcionando razonablemente bien. Lo que estoy buscando es un consejo útil con respecto a:
- Configuración de un proyecto de WiX (diseño, referencias, patrones de archivo)
- Integración de WiX en soluciones y procesos de compilación / lanzamiento.
- Configurando instaladores para nuevas instalaciones y actualizaciones.
- Cualquier buen hack de WiX que quieras compartir.
Imprimiendo EULA desde Wix3.0 y posteriores
1) Cuando compila el código fuente de su wix, light.exe debe hacer referencia a WixUIExtension.dll en la línea de comandos. Utilice el interruptor de línea de comandos -ext para esto.
2) Si cuando agrega la referencia a WixUIExtension.dll, su proyecto no se compila, es muy probable que se deba a un conflicto de ID de Diálogos, es decir, su proyecto estaba usando las mismas ID de diálogos que algunos diálogos estándar en WixUIExtension.dll, Da diferentes identificaciones a tus diálogos. Este es un problema bastante común.
3) El cuadro de diálogo de la licencia debe tener el control ScrollableText con el ID "LicenseText". Wix busca exactamente este nombre de control cuando se imprime.
<Control Id="LicenseText" Type="ScrollableText" X="20" Y="60" Width="330" Height="160" Sunken="yes" TabSkip="no">
<Text SourceFile="License.rtf" />
</Control>
y un PushButton que se refiere a la acción personalizada
<Control Type="PushButton" Id="PrintButton" Width="57" Height="17" X="19" Y="244" Text="Print">
<Publish Event="DoAction" Value="PrintEula">1</Publish>
</Control>
4) Defina CustomAction con el Id = "PrintEula" de esta manera:
<CustomAction Id="PrintEula" BinaryKey="WixUIWixca" DllEntry="PrintEula" Return="ignore" Execute="immediate" />
Nota: BinaryKey es diferente en Wix3.0 en comparación con Wix2.0 y debe ser exactamente "WixUIWixca" (distingue mayúsculas y minúsculas).
Cuando el usuario presiona el botón, se le presentará el diálogo de selección de impresora estándar y podrá imprimir desde allí.
Registro de ensamblados .NET para interoperabilidad COM con compatibilidad x86 / x64
NB: este fragmento es esencialmente el mismo que REGASM Assembly.dll / codebase
Hay un par de cosas en esta muestra, así que aquí está el código y lo explicaré más adelante ...
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?include $(sys.CURRENTDIR)/Config.wxi?>
<?if $(var.Win64) ?>
<?define CLSIDRoots = "CLSID;Wow6432Node/CLSID"?>
<?else ?>
<?define CLSIDRoots = "CLSID"?>
<?endif?>
<!-- ASCOM Driver Assembly with related COM registrations -->
<Fragment>
<DirectoryRef Id="INSTALLLOCATION" />
</Fragment>
<Fragment>
<ComponentGroup Id="cgAscomDriver">
<Component Id="cmpAscomDriver" Directory="INSTALLLOCATION" Guid="{0267031F-991D-4D88-A748-00EC6604171E}">
<File Id="filDriverAssembly" Source="$(var.TiGra.Astronomy.AWRDriveSystem.TargetPath)" KeyPath="yes" Vital="yes" Assembly=".net" AssemblyApplication="filDriverAssembly" />
<RegistryKey Root="HKCR" Key="$(var.DriverId)" Action="createAndRemoveOnUninstall">
<RegistryValue Type="string" Value="$(var.DriverTypeName)"/>
<RegistryKey Key="CLSID">
<RegistryValue Type="string" Value="$(var.DriverGuid)" />
</RegistryKey>
</RegistryKey>
<?foreach CLSID in $(var.CLSIDRoots) ?>
<RegistryKey Root="HKCR" Key="$(var.CLSID)" Action="none">
<RegistryKey Key="$(var.DriverGuid)" Action="createAndRemoveOnUninstall">
<RegistryValue Type="string" Value="$(var.DriverTypeName)"/>
<RegistryKey Key="InprocServer32">
<RegistryValue Type="string" Value="mscoree.dll" />
<RegistryValue Type="string" Name="ThreadingModel" Value="Both"/>
<RegistryValue Type="string" Name="Class" Value="$(var.DriverTypeName)"/>
<RegistryValue Type="string" Name="Assembly" Value="!(bind.assemblyFullname.filDriverAssembly)" />
<RegistryValue Type="string" Name="RuntimeVersion" Value="v2.0.50727"/>
<RegistryValue Type="string" Name="CodeBase" Value="file:///[#filDriverAssembly]" />
<RegistryKey Key="!(bind.fileVersion.filDriverAssembly)" >
<RegistryValue Type="string" Name="Class" Value="$(var.DriverTypeName)"/>
<RegistryValue Type="string" Name="Assembly" Value="!(bind.assemblyFullname.filDriverAssembly)" />
<RegistryValue Type="string" Name="RuntimeVersion" Value="v2.0.50727"/>
<RegistryValue Type="string" Name="CodeBase" Value="file:///[#filDriverAssembly]" />
</RegistryKey>
</RegistryKey>
<RegistryKey Key="ProgId" Action="createAndRemoveOnUninstall">
<RegistryValue Type="string" Value="$(var.DriverId)" />
</RegistryKey>
<RegistryKey Key="Implemented Categories" Action="createAndRemoveOnUninstall" >
<RegistryKey Key="{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}" Action="createAndRemoveOnUninstall" />
</RegistryKey>
</RegistryKey>
</RegistryKey>
<?endforeach?>
</Component>
</ComponentGroup>
</Fragment>
</Wix>
Si se lo preguntaba, esto es en realidad para un controlador de telescopio ASCOM .
Primero, tomé el consejo de arriba y creé algunas variables de plataforma en un archivo separado, puedes verlas dispersas a través del XML.
La parte if-then-else cerca de la parte superior tiene que ver con la compatibilidad entre x86 y x64. Mi ensamblaje apunta a ''Cualquier CPU'', así que en un sistema x64, necesito registrarlo dos veces, una en el registro de 64 bits y otra en las Wow6432Node
áreas de 32 bits . El if-then-else me configura para esto, los valores se utilizan en un foreach
bucle más adelante. De esta manera, solo tengo que crear las claves de registro una vez (principio DRY).
El elemento de archivo especifica el archivo DLL de ensamblaje real que se instala y registra:
<File Id="filDriverAssembly" Source="$(var.TiGra.Astronomy.AWRDriveSystem.TargetPath)" KeyPath="yes" Vital="yes" Assembly=".net" AssemblyApplication="filDriverAssembly" />
Nada revolucionario, pero observe que Assembly=".net"
, solo con este atributo, la asamblea se colocará en el GAC, que NO es lo que yo quería. Usar el AssemblyApplication
atributo para volver a apuntarse a sí mismo es simplemente una forma de detener a Wix poniendo el archivo en el GAC. Sin embargo, ahora que Wix sabe que es un ensamblado .net, me permite usar ciertas variables de enlace dentro de mi XML, como el !(bind.assemblyFullname.filDriverAssembly)
nombre completo del ensamblado.
Mantenga las variables en un archivo de inclusión
wxi
separado. Permite la reutilización, las variables son más rápidas de encontrar y (si es necesario) permite una manipulación más sencilla por parte de una herramienta externa.Definir variables de plataforma para compilaciones x86 y x64
<!-- Product name as you want it to appear in Add/Remove Programs--> <?if $(var.Platform) = x64 ?> <?define ProductName = "Product Name (64 bit)" ?> <?define Win64 = "yes" ?> <?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?> <?else ?> <?define ProductName = "Product Name" ?> <?define Win64 = "no" ?> <?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?> <?endif ?>
Almacene la ubicación de la instalación en el registro, permitiendo que las actualizaciones encuentren la ubicación correcta. Por ejemplo, si un usuario establece un directorio de instalación personalizado.
<Property Id="INSTALLLOCATION"> <RegistrySearch Id="RegistrySearch" Type="raw" Root="HKLM" Win64="$(var.Win64)" Key="Software/Company/Product" Name="InstallLocation" /> </Property>
Nota : el gurú de WiX, Rob Mensching, ha publicado una excelente entrada de blog que explica más detalladamente y corrige un caso límite cuando las propiedades se establecen desde la línea de comandos.
Ejemplos usando 1. 2. y 3.
<?include $(sys.CURRENTDIR)/Config.wxi?> <Product ... > <Package InstallerVersion="200" InstallPrivileges="elevated" InstallScope="perMachine" Platform="$(var.Platform)" Compressed="yes" Description="$(var.ProductName)" />
y
<Directory Id="TARGETDIR" Name="SourceDir"> <Directory Id="$(var.PlatformProgramFilesFolder)"> <Directory Id="INSTALLLOCATION" Name="$(var.InstallName)">
El enfoque más simple es hacer siempre actualizaciones importantes , ya que permite tanto nuevas instalaciones como actualizaciones en el MSI único. UpgradeCode está fijado a un Guid único y nunca cambiará, a menos que no queramos actualizar el producto existente.
Nota : ¡En WiX 3.5 hay un nuevo elemento MajorUpgrade que hace la vida aún más fácil !
Creación de un icono en Agregar o quitar programas
<Icon Id="Company.ico" SourceFile="../Tools/Company/Images/Company.ico" /> <Property Id="ARPPRODUCTICON" Value="Company.ico" /> <Property Id="ARPHELPLINK" Value="http://www.example.com/" />
En las compilaciones de lanzamiento, versionamos nuestros instaladores, copiando el archivo msi a un directorio de implementación. Un ejemplo de esto utilizando un objetivo wixproj llamado desde el objetivo de AfterBuild:
<Target Name="CopyToDeploy" Condition="''$(Configuration)'' == ''Release''"> <!-- Note we append AssemblyFileVersion, changing MSI file name only works with Major Upgrades --> <Copy SourceFiles="$(OutputPath)$(OutputName).msi" DestinationFiles="../Deploy/Setup/$(OutputName) $(AssemblyFileVersion)_$(Platform).msi" /> </Target>
Usa calor para cosechar archivos con comodines (*) Guid. Útil si desea reutilizar archivos WXS en múltiples proyectos (vea mi respuesta en varias versiones del mismo producto). Por ejemplo, este archivo por lotes cosecha automáticamente la salida de RoboHelp.
@echo off robocopy ../WebHelp "%TEMP%/WebHelpTemp/WebHelp" /E /NP /PURGE /XD .svn "%WIX%bin/heat" dir "%TEMP%/WebHelp" -nologo -sfrag -suid -ag -srd -dir WebHelp -out WebHelp.wxs -cg WebHelpComponent -dr INSTALLLOCATION -var var.WebDeploySourceDir
robocopy
está sucediendo,robocopy
está eliminando los metadatos de las copias de trabajo de Subversion antes de la recolección; la referencia del directorio raíz-dr
se establece en nuestra ubicación de instalación en lugar de TARGETDIR predeterminado;-var
se utiliza para crear una variable para especificar el directorio de origen (salida de despliegue web).Una forma sencilla de incluir la versión del producto en el título del diálogo de bienvenida utilizando Strings.wxl para la localización. (Crédito: saschabeaumont . Agregado ya que este gran consejo está oculto en un comentario)
<WixLocalization Culture="en-US" xmlns="http://schemas.microsoft.com/wix/2006/localization"> <String Id="WelcomeDlgTitle">{/WixUI_Font_Bigger}Welcome to the [ProductName] [ProductVersion] Setup Wizard</String> </WixLocalization>
Ahórrese un poco de dolor y siga los consejos de Wim Coehen de un componente por archivo. Esto también le permite omitir (o comodín
*
) el GUID del componente .Rob Mensching tiene una forma ingeniosa de rastrear rápidamente los problemas en los archivos de registro de MSI buscando el
value 3
. Tenga en cuenta los comentarios relativos a la internacionalización.Al agregar funciones condicionales, es más intuitivo establecer el nivel de función predeterminado en 0 (deshabilitado) y luego establecer el nivel de condición en el valor deseado. Si establece el nivel de función predeterminado> = 1, el nivel de condición debe ser 0 para deshabilitarlo, lo que significa que la lógica de la condición debe ser opuesta a lo que usted esperaría, lo que puede ser confuso :)
<Feature Id="NewInstallFeature" Level="0" Description="New installation feature" Absent="allow"> <Condition Level="1">NOT UPGRADEFOUND</Condition> </Feature> <Feature Id="UpgradeFeature" Level="0" Description="Upgrade feature" Absent="allow"> <Condition Level="1">UPGRADEFOUND</Condition> </Feature>
Comprobando si IIS está instalado:
<Property Id="IIS_MAJOR_VERSION">
<RegistrySearch Id="CheckIISVersion" Root="HKLM" Key="SOFTWARE/Microsoft/InetStp" Name="MajorVersion" Type="raw" />
</Property>
<Condition Message="IIS must be installed">
Installed OR IIS_MAJOR_VERSION
</Condition>
Comprobando si la compatibilidad con la metabase de IIS 6 está instalada en Vista +:
<Property Id="IIS_METABASE_COMPAT">
<RegistrySearch Id="CheckIISMetabase" Root="HKLM" Key="SOFTWARE/Microsoft/InetStp/Components" Name="ADSICompatibility" Type="raw" />
</Property>
<Condition Message="IIS 6 Metabase Compatibility feature must be installed">
Installed OR ((VersionNT < 600) OR IIS_METABASE_COMPAT)
</Condition>
Fantástica pregunta. Me encantaría ver algunas de las mejores prácticas mostradas.
Tengo muchos archivos que distribuyo, así que configuré mi proyecto en varios archivos de origen wxs.
Tengo un archivo fuente de nivel superior al que llamo Product.wxs que básicamente contiene la estructura para la instalación, pero no los componentes reales. Este archivo tiene varias secciones:
<Product ...>
<Package ...>
<Media>...
<Condition>s ...
<Upgrade ..>
<Directory>
...
</Directory>
<Feature>
<ComponentGroupRef ... > A bunch of these that
</Feature>
<UI ...>
<Property...>
<Custom Actions...>
<Install Sequences....
</Package>
</Product>
El resto de los archivos .wix están compuestos por Fragmentos que contienen Grupos de Componentes a los que se hace referencia en la etiqueta de Característica en el Product.wxs. Mi proyecto contiene una buena agrupación lógica de los archivos que distribuyo
<Fragment>
<ComponentGroup>
<ComponentRef>
....
</ComponentGroup>
<DirectoryRef>
<Component... for each file
....
</DirectoryRef>
</Fragment>
Esto no es perfecto, mi sentido de araña OO se estremece un poco porque los fragmentos tienen que hacer referencia a nombres en el archivo Product.wxs (por ejemplo, DirectoryRef), pero me parece más fácil mantener ese único archivo de origen grande.
¡Me encantaría escuchar comentarios sobre esto, o si alguien tiene buenos consejos también!
Me sorprende que nadie haya mencionado el uso de T4 para generar el archivo WXS durante la compilación. Aprendí sobre esto a través de Henry Lee en New Age Solutions .
Esencialmente, creas una tarea MSBuild personalizada para ejecutar una plantilla T4, y esa plantilla genera el WXS justo antes de compilar el proyecto Wix. Esto le permite (dependiendo de cómo lo implemente) incluir automáticamente todos los resultados de los ensamblajes de la compilación de otra solución (lo que significa que ya no tendrá que editar los wxs cada vez que agregue un ensamblaje nuevo).
Peter Tate ya ha mostrado cómo puede definir definiciones de ComponentGroup reutilizables en fragmentos de wix separados. Algunos trucos adicionales relacionados con esto:
Alias de directorio
Los fragmentos del grupo de componentes no necesitan conocer los directorios definidos por el producto principal wxs. En el fragmento de su grupo de componentes puede hablar sobre una carpeta como esta:
<DirectoryRef Id="component1InstallFolder">
...
</DirectoryRef>
Luego, el producto principal puede crear un alias de uno de sus directorios (por ejemplo, "productInstallFolder") como este:
<Directory Id="productInstallFolder" Name="ProductName">
<!-- not subfolders (because no Name attribute) but aliases for parent! -->
<Directory Id="component1InstallFolder"/>
<Directory Id="component2InstallFolder"/>
</Directory>
Gráfico de dependencia
Los elementos ComponentGroup pueden contener elementos secundarios ComponentGroupRef. Esto es excelente si tiene una gran cantidad de componentes reutilizables con un complejo gráfico de dependencia entre ellos. Simplemente configura un ComponentGroup en su propio fragmento para cada componente y declara las dependencias de esta manera:
<ComponentGroup Id="B">
<ComponentRef Id="_B" />
<ComponentGroupRef Id="A">
</ComponentGroup>
Si ahora hace referencia al grupo de componentes "B" en su configuración porque es una dependencia directa de su aplicación, automáticamente obtendrá el grupo de componentes "A" incluso si el autor de la aplicación nunca se dio cuenta de que era una dependencia de "B". "Simplemente funciona" siempre que no tenga dependencias circulares.
Wixlib reutilizable
La idea del gráfico de dependencia anterior funciona mejor si compila los componentes big-pool-o-reutilizable en un wixlib reutilizable con lit.exe. Al crear una configuración de aplicación, puede hacer referencia a este wixlib como un archivo wixobj. El enlazador candle.exe eliminará automáticamente cualquier fragmento que no sea "arrastrado" por los archivos wxs del producto principal.
Here''s una manera de ayudar a grandes proyectos web a verificar que la cantidad de archivos implementados coincida con la cantidad de archivos incorporados en un MSI (o módulo de combinación). Acabo de ejecutar la tarea personalizada de MSBuild en nuestra aplicación principal (aún en desarrollo) y detecté algunos archivos faltantes, en su mayoría imágenes, ¡pero algunos archivos javascript se habían deslizado hasta!
Este enfoque (que se asoma a la tabla de archivos de MSI al enlazar con el proyecto de WiX de AfterBuild) podría funcionar para otros tipos de aplicaciones donde tenga acceso a una lista completa de los archivos esperados.
Incluyendo objetos COM:
heat
genera la mayoría (si no todas) las entradas de registro y otras configuraciones necesarias para ellas. ¡Alegrarse!
Incluyendo objetos COM administrados (objetos aka, .NET o C # COM)
El uso de heat
en un objeto COM administrado le dará un documento wix casi completo.
Si no necesita la biblioteca disponible en el GAC (es decir, disponible a nivel mundial: la mayoría de las veces no necesita esto con sus ensamblados .NET de todos modos; probablemente haya hecho algo incorrecto en este momento si no se pretende que sea una biblioteca compartida) querrá asegurarse de actualizar la clave de registro de CodeBase
para que se establezca en [#ComponentName]
. Si ESTÁ planeando instalarlo en el GAC (por ejemplo, ha creado una nueva biblioteca común impresionante que todos querrán usar), debe eliminar esta entrada y agregar dos nuevos atributos al elemento File
: Assembly
y KeyPath
. El ensamblaje debe establecerse en ".net" y KeyPath
debe configurarse en "sí".
Sin embargo, algunos entornos (especialmente cualquier cosa con memoria administrada, como los lenguajes de scripting) también necesitarán acceso a Typelib. Asegúrese de poner heat
en su biblioteca de caracteres e incluirlo. heat
generará todas las claves de registro necesarias. ¿Cuan genial es eso?
Realizar una reinstalación forzada cuando una instalación no permite desinstalar o reinstalar y no retroceder.
La secuencia de comandos VBscript utilizada para anular una instalación que no se desinstala por cualquier motivo.
Dim objShell
set objShell = wscript.createObject("wscript.shell")
iReturn = objShell.Run("CMD /K MsiExec.exe /I ""C:/Users/TheUser/Documents/Visual Studio 2010/Projects/InstallationTarget/HelloInstaller/bin/Debug/HelloInstaller.msi"" REINSTALLMODE=vomus REINSTALL=ALL",,True)
Usar Heat.exe para destrozar la cara e infligir "Epic Pwnage" en instalaciones terriblemente grandes
Ampliando Robert-P''s respuestas de Si''s y Robert-P''s sobre el calor.
Traducción: (Uso de calor para evitar escribir archivos individuales en el proyecto a mano y para automatizar compilaciones para un proceso global más fácil).
heat.exe
Para las versiones más nuevas (no muy diferentes de las versiones anteriores, pero hay cambios de sintaxis potencialmente molestos ...) vaya al directorio Heat desde el cmd.exe y simplemente escriba en heat pero tengo un ejemplo aquí para obtener ayuda. con versiones más nuevas si es necesario.
Agregando lo siguiente a su evento de construcción en visual studio 2010.
(Haga clic derecho en Proyecto-> Propiedades -> Eventos de compilación-> Eventos de precompilación)
$(WIX)bin/heat.exe" dir "$(EnviromentVariable)" -cg GroupVariable -gg -scom -sreg -sfrag - srd -dr INSTALLLOCATION -var env.LogicPath -out "$(FragmentDir)/FileName.wxs
-gg
Genera Guías cuando se ejecuta calor (como cuando ejecutas el comando anterior)
-scom
No agarres "archivos COM"
-sreg
No agarres "Archivos de Registro"
-sfrag
No agarres "Fragmentos"
-srd
No agarres la "raíz Dir"
dir
dir indica que quieres que Heat se vea en una carpeta
"$(EnviromentVariable)"
El nombre de la variable que agregaría a las variables del preprocesador en el proyecto (haga clic con el botón derecho, ir a propiedades) propiedades del proyecto-> Crear sección donde dice Definir las variables del preprocesador (asume visual studio 2010)
Example: EnviromentVariable=C:/Project/bin/Debug; No hay comillas dobles pero termina con un punto y coma.
-cg GroupVariable
El ComponentGroup al que se hará referencia desde el fragmento creado al archivo principal wxs
FragmentDir
El directorio de fragmentos donde se almacenará el fragmento de salida wxs
FileName.wxs
El nombre del archivo.
Tutorial completo aquí, muy útil
Uso del registro de diagnóstico Msi para obtener información detallada sobre fallas
msiexec /i Package.msi /l*vc:/Package.log
Dónde
Package.msi es el nombre de tu paquete y
c:/Package.log Es donde quieres la salida del log.
Wix Intro Video
Ah, y el vídeo de introducción de Random Wix con "Mr. WiX" Rob Mensching es útil para "la gran imagen conceptual".
Agregue una casilla de verificación al cuadro de diálogo de salida para iniciar la aplicación o el archivo de ayuda.
...
<!-- CA to launch the exe after install -->
<CustomAction Id ="CA.StartAppOnExit"
FileKey ="YourAppExeId"
ExeCommand =""
Execute ="immediate"
Impersonate ="yes"
Return ="asyncNoWait" />
<!-- CA to launch the help file -->
<CustomAction Id ="CA.LaunchHelp"
Directory ="INSTALLDIR"
ExeCommand =''[WindowsFolder]hh.exe IirfGuide.chm''
Execute ="immediate"
Return ="asyncNoWait" />
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT"
Value="Launch MyApp when setup exits." />
<UI>
<Publish Dialog ="ExitDialog"
Control ="Finish"
Order ="1"
Event ="DoAction"
Value ="CA.StartAppOnExit">WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT</Publish>
</UI>
Si lo haces de esta manera, el aspecto "estándar" no es del todo correcto. La casilla de verificación siempre es un fondo gris, mientras que el cuadro de diálogo es blanco:
Una forma de evitar esto es especificar su propio ExitDialog personalizado, con una casilla de verificación ubicada de manera diferente . Esto funciona, pero parece mucho trabajo solo para cambiar el color de un control. Otra forma de resolver lo mismo es post-procesar el MSI generado para cambiar los campos X, Y en la tabla de Control para ese control CheckBox en particular. El código javascript se ve así:
var msiOpenDatabaseModeTransact = 1;
var filespec = WScript.Arguments(0);
var installer = new ActiveXObject("WindowsInstaller.Installer");
var database = installer.OpenDatabase(filespec, msiOpenDatabaseModeTransact);
var sql = "UPDATE `Control` SET `Control`.`Height` = ''18'', `Control`.`Width` = ''170''," +
" `Control`.`Y`=''243'', `Control`.`X`=''10'' " +
"WHERE `Control`.`Dialog_`=''ExitDialog'' AND " +
" `Control`.`Control`=''OptionalCheckBox''";
var view = database.OpenView(sql);
view.Execute();
view.Close();
database.Commit();
Ejecutar este código como un script de línea de comandos (usando cscript.exe) después de que se genere el MSI (de light.exe) producirá un ExitDialog que parece más profesional:
Coloque los componentes que pueden ser parcheados individualmente dentro de sus propios fragmentos.
Se aplica tanto a los instaladores de productos como a los parches que, si incluye cualquier componente en un fragmento, debe incluir todos los componentes en ese fragmento. En el caso de crear un instalador, si pierde alguna referencia de componente, obtendrá un error de enlace de light.exe. Sin embargo, cuando crea un parche, si incluye una sola referencia de componente en un fragmento, todos los componentes modificados de ese fragmento se mostrarán en su parche.
Me gusta esto:
<Fragment>
<DirectoryRef Id="SampleProductFolder">
<Component Id="SampleComponent1" Guid="{C28843DA-EF08-41CC-BA75-D2B99D8A1983}" DiskId="1">
<File Id="SampleFile1" Source="./$(var.Version)f/Sample1.txt" />
</Component>
</DirectoryRef>
</Fragment>
<Fragment>
<DirectoryRef Id="SampleProductFolder">
<Component Id="SampleComponent2" Guid="{6CEA5599-E7B0-4D65-93AA-0F2F64402B22}" DiskId="1">
<File Id="SampleFile2" Source="./$(var.Version)f/Sample2.txt" />
</Component>
</DirectoryRef>
</Fragment>
<Fragment>
<DirectoryRef Id="SampleProductFolder">
<Component Id="SampleComponent3" Guid="{4030BAC9-FAB3-426B-8D1E-DC1E2F72C2FC}" DiskId="1">
<File Id="SampleFile3" Source="./$(var.Version)f/Sample3.txt" />
</Component>
</DirectoryRef>
</Fragment>
en lugar de esto:
<Fragment>
<DirectoryRef Id="SampleProductFolder">
<Component Id="SampleComponent1" Guid="{C28843DA-EF08-41CC-BA75-D2B99D8A1983}" DiskId="1">
<File Id="SampleFile1" Source="./$(var.Version)/Sample1.txt" />
</Component>
<Component Id="SampleComponent2" Guid="{6CEA5599-E7B0-4D65-93AA-0F2F64402B22}" DiskId="1">
<File Id="SampleFile2" Source="./$(var.Version)/Sample2.txt" />
</Component>
<Component Id="SampleComponent3" Guid="{4030BAC9-FAB3-426B-8D1E-DC1E2F72C2FC}" DiskId="1">
<File Id="SampleFile3" Source="./$(var.Version)/Sample3.txt" />
</Component>
</DirectoryRef>
</Fragment>
Además, cuando realice la aplicación de parches utilizando el tema "Uso de Purely WiX" del archivo de ayuda de WiX.chm, use este procedimiento para generar el parche:
torch.exe -p -xi 1.0/product.wixpdb 1.1/product.wixpdb -out patch/diff.wixmst
candle.exe patch.wxs
light.exe patch.wixobj -out patch/patch.wixmsp
pyro.exe patch/patch.wixmsp -out patch/patch.msp -t RTM patch/diff.wixmst
no es suficiente tener la versión 1.1 del producto.wixpdb construido usando los componentes en fragmentos separados. Así que asegúrese de fragmentar correctamente su producto antes de enviarlo.
Edición de diálogos
Una buena capacidad para editar diálogos es usar SharpDevelop en una versión 4.0.1.7090 (o superior). Con la ayuda de esta herramienta, se puede abrir, previsualizar y editar en la vista Diseño un cuadro de diálogo independiente (archivos wxs de fuentes WiX como, por ejemplo, InstallDirDlg.wxs).
Instalación en C:/ProductName
Algunas aplicaciones deben instalarse en C:/ProductName
o algo similar, pero el 99.9% (si no es el 100%) de los ejemplos en la red, instalar en C:/Program Files/CompanyName/ProductName
.
El siguiente código se puede usar para establecer la propiedad TARGETDIR
en la raíz de la unidad C:
(tomada de la lista de usuarios de WiX ):
<CustomAction Id="AssignTargetDir" Property="TARGETDIR" Value="C:/" Execute="firstSequence" />
<InstallUISequence>
<Custom Action="AssignTargetDir" Before="CostInitialize">TARGETDIR=""</Custom>
</InstallUISequence>
<InstallExecuteSequence>
<Custom Action="AssignTargetDir" Before="CostInitialize">TARGETDIR=""</Custom>
</InstallExecuteSequence>
NOTA: De forma predeterminada, TARGETDIR
no apunta a C:/
! Más bien apunta a ROOTDRIVE
que, a su vez, apunta a la raíz de la unidad con el mayor espacio libre ( consulte aquí ), y esto no es necesariamente la unidad C:
. ¡Puede haber otro disco duro, partición o unidad USB!
Luego, en algún lugar debajo de su etiqueta <Product ...>
, necesita las siguientes etiquetas de directorio como de costumbre:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="APPLICATIONFOLDER" Name="$(var.ProductName)">
<!-- your content goes here... -->
</Directory>
</Directory>
Mantenga todas las identificaciones en espacios de nombres separados
- Las características comienzan con
F.
Ejemplos: F.Documentation, F.Binaries, F.SampleCode. - Los componentes comienzan con
C.
Ej .: C.ChmFile, C.ReleaseNotes, C.LicenseFile, C.IniFile, C.Registry - Las acciones personalizadas son
CA.
Ej: CA.LaunchHelp, CA.UpdateReadyDlg, CA.SetPropertyX - Los archivos son
Fi.
- Los directorios son
Di.
- y así.
Me parece que esto ayuda enormemente a realizar un seguimiento de todos los ID diferentes en todas las diferentes categorías.
Modificar el "¿Listo para instalar?" diálogo (también conocido como VerifyReadyDlg) para proporcionar un resumen de las elecciones realizadas.
Se parece a esto:
texto alt http://i46.tinypic.com/s4th7t.jpg
Haz esto con un Javascript CustomAction:
Código Javascript:
// http://msdn.microsoft.com/en-us/library/aa372516(VS.85).aspx
var MsiViewModify =
{
Refresh : 0,
Insert : 1,
Update : 2,
Assign : 3,
Replace : 4,
Merge : 5,
Delete : 6,
InsertTemporary : 7, // cannot permanently modify the MSI during install
Validate : 8,
ValidateNew : 9,
ValidateField : 10,
ValidateDelete : 11
};
// http://msdn.microsoft.com/en-us/library/sfw6660x(VS.85).aspx
var Buttons =
{
OkOnly : 0,
OkCancel : 1,
AbortRetryIgnore : 2,
YesNoCancel : 3
};
var Icons=
{
Critical : 16,
Question : 32,
Exclamation : 48,
Information : 64
}
var MsgKind =
{
Error : 0x01000000,
Warning : 0x02000000,
User : 0x03000000,
Log : 0x04000000
};
// http://msdn.microsoft.com/en-us/library/aa371254(VS.85).aspx
var MsiActionStatus =
{
None : 0,
Ok : 1, // success
Cancel : 2,
Abort : 3,
Retry : 4, // aka suspend?
Ignore : 5 // skip remaining actions; this is not an error.
};
function UpdateReadyDialog_CA(sitename)
{
try
{
// can retrieve properties from the install session like this:
var selectedWebSiteId = Session.Property("MSI_PROPERTY_HERE");
// can retrieve requested feature install state like this:
var fInstallRequested = Session.FeatureRequestState("F.FeatureName");
var text1 = "This is line 1 of text in the VerifyReadyDlg";
var text2 = "This is the second line of custom text";
var controlView = Session.Database.OpenView("SELECT * FROM Control");
controlView.Execute();
var rec = Session.Installer.CreateRecord(12);
rec.StringData(1) = "VerifyReadyDlg"; // Dialog_
rec.StringData(2) = "CustomVerifyText1"; // Control - can be any name
rec.StringData(3) = "Text"; // Type
rec.IntegerData(4) = 25; // X
rec.IntegerData(5) = 60; // Y
rec.IntegerData(6) = 320; // Width
rec.IntegerData(7) = 85; // Height
rec.IntegerData(8) = 2; // Attributes
rec.StringData(9) = ""; // Property
rec.StringData(10) = vText1; // Text
rec.StringData(11) = ""; // Control_Next
rec.StringData(12) = ""; // Help
controlView.Modify(MsiViewModify.InsertTemporary, rec);
rec = Session.Installer.CreateRecord(12);
rec.StringData(1) = "VerifyReadyDlg"; // Dialog_
rec.StringData(2) = "CustomVerifyText2"; // Control - any unique name
rec.StringData(3) = "Text"; // Type
rec.IntegerData(4) = 25; // X
rec.IntegerData(5) = 160; // Y
rec.IntegerData(6) = 320; // Width
rec.IntegerData(7) = 65; // Height
rec.IntegerData(8) = 2; // Attributes
rec.StringData(9) = ""; // Property
rec.StringData(10) = text2; // Text
rec.StringData(11) = ""; // Control_Next
rec.StringData(12) = ""; // Help
controlView.Modify(MsiViewModify.InsertTemporary, rec);
controlView.Close();
}
catch (exc1)
{
Session.Property("CA_EXCEPTION") = exc1.message ;
LogException("UpdatePropsWithSelectedWebSite", exc1);
return MsiActionStatus.Abort;
}
return MsiActionStatus.Ok;
}
function LogException(loc, exc)
{
var record = Session.Installer.CreateRecord(0);
record.StringData(0) = "Exception {" + loc + "}: " + exc.number + " : " + exc.message;
Session.Message(MsgKind.Error + Icons.Critical + Buttons.btnOkOnly, record);
}
Declare el Javascript CA:
<Fragment>
<Binary Id="IisScript_CA" SourceFile="CustomActions.js" />
<CustomAction Id="CA.UpdateReadyDialog"
BinaryKey="IisScript_CA"
JScriptCall="UpdateReadyDialog_CA"
Execute="immediate"
Return="check" />
</Fragment>
Adjuntar la CA a un botón. En este ejemplo, la CA se activa cuando se hace clic en Siguiente desde CustomizeDlg:
<UI ...>
<Publish Dialog="CustomizeDlg" Control="Next" Event="DoAction"
Value="CA.UpdateReadyDialog" Order="1"/>
</UI>
Pregunta de SO relacionada: ¿Cómo puedo configurar, en tiempo de ejecución, el texto que se mostrará en VerifyReadyDlg?
Usa Javascript CustomActions porque son muy fáciles
La gente ha dicho que Javascript es lo incorrecto de usar para MSI CustomActions . Razones dadas: difíciles de depurar, difíciles de hacer fiables. No estoy de acuerdo No es difícil de depurar, ciertamente no es más difícil que C ++. Es simplemente diferente. Encontré que escribir CustomActions en Javascript es súper fácil, mucho más fácil que usar C ++. Mucho mas rápido. Y tan confiable.
Solo hay un inconveniente: Javascript CustomActions se puede extraer a través de Orca, mientras que un C / C ++ CA requeriría ingeniería inversa. Si considera que su instalador es una propiedad intelectual protegida, querrá evitar el script.
Si usa un script, solo necesita comenzar con alguna estructura. Aquí hay algunos para empezar.
Código "boilerplate" de Javascript para CustomAction:
//
// CustomActions.js
//
// Template for WIX Custom Actions written in Javascript.
//
//
// Mon, 23 Nov 2009 10:54
//
// ===================================================================
// http://msdn.microsoft.com/en-us/library/sfw6660x(VS.85).aspx
var Buttons = {
OkOnly : 0,
OkCancel : 1,
AbortRetryIgnore : 2,
YesNoCancel : 3
};
var Icons = {
Critical : 16,
Question : 32,
Exclamation : 48,
Information : 64
};
var MsgKind = {
Error : 0x01000000,
Warning : 0x02000000,
User : 0x03000000,
Log : 0x04000000
};
// http://msdn.microsoft.com/en-us/library/aa371254(VS.85).aspx
var MsiActionStatus = {
None : 0,
Ok : 1, // success
Cancel : 2,
Abort : 3,
Retry : 4, // aka suspend?
Ignore : 5 // skip remaining actions; this is not an error.
};
function MyCustomActionInJavascript_CA() {
try {
LogMessage("Hello from MyCustomActionInJavascript");
// ...do work here...
LogMessage("Goodbye from MyCustomActionInJavascript");
}
catch (exc1) {
Session.Property("CA_EXCEPTION") = exc1.message ;
LogException(exc1);
return MsiActionStatus.Abort;
}
return MsiActionStatus.Ok;
}
// Pop a message box. also spool a message into the MSI log, if it is enabled.
function LogException(exc) {
var record = Session.Installer.CreateRecord(0);
record.StringData(0) = "CustomAction: Exception: 0x" + decimalToHexString(exc.number) + " : " + exc.message;
Session.Message(MsgKind.Error + Icons.Critical + Buttons.btnOkOnly, record);
}
// spool an informational message into the MSI log, if it is enabled.
function LogMessage(msg) {
var record = Session.Installer.CreateRecord(0);
record.StringData(0) = "CustomAction:: " + msg;
Session.Message(MsgKind.Log, record);
}
// http://msdn.microsoft.com/en-us/library/d5fk67ky(VS.85).aspx
var WindowStyle = {
Hidden : 0,
Minimized : 1,
Maximized : 2
};
// http://msdn.microsoft.com/en-us/library/314cz14s(v=VS.85).aspx
var OpenMode = {
ForReading : 1,
ForWriting : 2,
ForAppending : 8
};
// http://msdn.microsoft.com/en-us/library/a72y2t1c(v=VS.85).aspx
var SpecialFolders = {
WindowsFolder : 0,
SystemFolder : 1,
TemporaryFolder : 2
};
// Run a command via cmd.exe from within the MSI
function RunCmd(command)
{
var wshell = new ActiveXObject("WScript.Shell");
var fso = new ActiveXObject("Scripting.FileSystemObject");
var tmpdir = fso.GetSpecialFolder(SpecialFolders.TemporaryFolder);
var tmpFileName = fso.BuildPath(tmpdir, fso.GetTempName());
LogMessage("shell.Run("+command+")");
// use cmd.exe to redirect the output
var rc = wshell.Run("%comspec% /c " + command + "> " + tmpFileName, WindowStyle.Hidden, true);
LogMessage("shell.Run rc = " + rc);
// here, optionally parse the output of the command
if (parseOutput) {
var textStream = fso.OpenTextFile(tmpFileName, OpenMode.ForReading);
while (!textStream.AtEndOfStream) {
var oneLine = textStream.ReadLine();
var line = ParseOneLine(oneLine);
...
}
textStream.Close();
}
if (deleteOutput) {
fso.DeleteFile(tmpFileName);
}
return {
rc : rc,
outputfile : (deleteOutput) ? null : tmpFileName
};
}
Luego, registra la acción personalizada con algo como esto:
<Fragment>
<Binary Id="IisScript_CA" SourceFile="CustomActions.js" />
<CustomAction Id="CA.MyCustomAction"
BinaryKey="IisScript_CA"
JScriptCall="MyCustomActionInJavascript_CA"
Execute="immediate"
Return="check" />
</Fragmemt>
Por supuesto, puede insertar tantas funciones de Javascript como desee, para múltiples acciones personalizadas. Un ejemplo: usé Javascript para hacer una consulta de WMI en IIS, para obtener una lista de sitios web existentes, en los que se podría instalar un filtro ISAPI. Esta lista se usó para completar un cuadro de lista que se muestra más adelante en la secuencia de la interfaz de usuario. Todo muy fácil.
En IIS7, no hay un proveedor de WMI para IIS, así que usé el enfoque shell.Run()
para invocar appcmd.exe para realizar el trabajo. Fácil.
Pregunta relacionada: Acerca de Javascript CustomActions
Variables ambientales
Al compilar sus documentos Wxs en código wixobj, puede utilizar variables de entorno para determinar información diversa. Por ejemplo, supongamos que desea cambiar qué archivos se incluyen en un proyecto. Digamos que tiene una variable de entorno llamada RELEASE_MODE, que configura justo antes de construir su MSI (ya sea con un script o manualmente, no importa) En su fuente de wix, puede hacer algo como:
<define FILESOURCE = c:/source/output/bin/$(env.RELEASE_MODE) >
y luego, más adelante en su código, utilícelo en el lugar para cambiar al instante su documento wxs, por ejemplo:
<Icon Id="myicon.ico" SourceFile="$(var.FILESOURCE)" />
Antes de desplegar un paquete de instalación, siempre controlo el contenido del mismo.
Es solo una simple llamada en la línea de comando (según Terrences post) abre la línea de comando y entra
msiexec /a Package.msi /qb TARGETDIR="%CD%/Extract" /l*vx "%CD/install.log%"
Esto extraerá el contenido del paquete a un subdir ''Extract'' con la ruta actual.
Creación de acción personalizada para WIX escrita en código administrado (C #) sin Votive
Cree una IU que tenga una acción personalizada que establecerá una variable y la IU deshabilitará / habilitará el siguiente botón (o similar) basado en la variable establecida en la acción personalizada.
No es tan sencillo como podría pensar, ¡no es demasiado difícil, simplemente no está documentado en ninguna parte!
Interacciones Wix con condiciones, propiedades y acciones personalizadas
Es una buena estructura pero, según mi experiencia, me pregunto cómo aborda estas condiciones:
A. Tus instalaciones parecen aparecer en el mismo destino. Si un usuario necesita instalar las 3 versiones a la vez, su proceso lo permitirá. ¿Pueden decir inequívocamente qué versión de cada ejecutable están activando?
B. ¿Cómo maneja los archivos nuevos que existen en TEST y / o TRAINING pero aún no en LIVE?
Configuración del indicador de IIS enable32BitAppOnWin64 http://trycatchfail.com/blog/post/WiX-Snippet-change-enable32BitAppOnWin64.aspx
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallFinalize" />
<Custom Action="ConfigureAppPool" After="InstallFinalize" >
<![CDATA[NOT Installed AND VersionNT64 >= 600]]>
</Custom>
</InstallExecuteSequence>
<CustomAction Id="ConfigureAppPool" Return="check" Directory="TARGETDIR" ExeCommand="[SystemFolder]inetsrv/appcmd set apppool /apppool.name:[APPPOOLNAME] /enable32BitAppOnWin64:false" />
En lugar de ORCA, use InstEd que es una buena herramienta para ver tablas MSI. También tiene la capacidad de diferenciar dos paquetes por Transformar -> Comparar con ...
Además está disponible una versión Plus con funcionalidad adicional. Pero también la versión gratuita ofrece una buena alternativa para Orca.
Establezca la DISABLEADVTSHORTCUTS
propiedad para forzar que todos los accesos directos anunciados en su instalador se conviertan en accesos directos regulares, y no necesita incluir una clave de registro ficticia para usarla como ruta de acceso clave.
<Property Id="DISABLEADVTSHORTCUTS" Value="1"/>
Creo que Windows Installer 4.0 o superior es un requirement .
Utilizando el RobM especial "Recuerde que la propiedad" patrón
http://robmensching.com/blog/posts/2010/5/2/The-WiX-toolsets-Remember-Property-pattern
Mostramos la versión del producto en algún lugar (pequeña) en la primera pantalla de la GUI. Porque las personas tienden a cometer errores al escoger la versión correcta cada vez. (Y nos mantienen los desarrolladores buscando por edades ...)
Hemos configurado TFSBuild para generar también transformaciones (archivos .mst) con la configuración para nuestros diferentes entornos. (Conocemos todos los entornos que necesitamos implementar).
Dado que la publicación de weblog original de Grant Holliday está abajo, copié el contenido pegado aquí:
Tarea de MSBuild para generar archivos MSI Transform desde XMLMarch 11 2008
En mi publicación anterior, describí cómo puede usar los archivos MSI Transform (* .mst) para separar las configuraciones específicas del entorno de un paquete MSI genérico.
Aunque esto proporciona un nivel de flexibilidad en su configuración, hay dos desventajas de los archivos Transform:
- Son un formato binario.
- No puede "editar" o "ver" un archivo de transformación. Debe aplicarlo o volver a crearlo para ver qué cambios incluye.
Afortunadamente, podemos usar la Biblioteca de objetos de Microsoft Windows Installer (c: windowssystem32msi.dll) para abrir las "bases de datos" de MSI y crear archivos de transformación.
Los créditos van de nuevo a Alex Shevchuk - De MSI a WiX - Parte 7 - Personalizando la instalación usando Transforms para mostrarnos cómo lograr esto con VbScript. Esencialmente, todo lo que he hecho es tomar el ejemplo de Alex y usar Interop.WindowsInstaller.dll. He implementado una tarea de MSBuild. La tarea de MSBuild
Download el código fuente y el ejemplo transforms.xml aquí (~ 7Kb de la Solución VS2008 comprimida)