Trucos y consejos de WiX

264

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 son consejos útiles con respecto a:

  • Configuración de un proyecto WiX (diseño, referencias, patrones de archivos)
  • Integrando WiX en soluciones y procesos de construcción / lanzamiento
  • Configurar instaladores para nuevas instalaciones y actualizaciones
  • Cualquier buen truco de WiX que quieras compartir
si618
fuente
eche un vistazo a gui4wix.codeplex.com
TarunG
10
Cerrado como no constructivo? ¡Aprendí un montón al hacer esta pregunta! Un poco de coherencia de StackOverflow también sería bueno ... por ejemplo stackoverflow.com/questions/550632/…
si618
15
Obtuvo '203' Ups, eso es suficiente para demostrar su utilidad.
TarunG
Las preguntas SO deben tener una respuesta definitiva y correcta; Las preguntas abiertas hacen que las preguntas que las personas hacen sobre los problemas reales salgan de la página principal. faq @Si .: Esa política siempre ha estado ahí AFAIK, pero ahora se aplica mejor; esa pregunta tiene casi tres años.
Jim Dagg
Bastante justo Jim, es una pregunta abierta, y supongo que depende de la comunidad SO decidir, pero tengo que decir que cerrarlo como no constructivo parece extraño, dado que yo y por lo que parece, muchas otras personas He encontrado esta pregunta útil (por ejemplo, goo.gl/Zqp2X ), y que encaja muy bien con la practical, answerable questions based on actual problems that you faceparte de las preguntas frecuentes.
si618

Respuestas:

157
  1. Mantenga las variables en un wxiarchivo de inclusión separado . Permite la reutilización, las variables son más rápidas de encontrar y (si es necesario) permite una manipulación más fácil por una herramienta externa.

  2. 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 ?>
    
  3. Almacene la ubicación de instalación en el registro, permitiendo actualizaciones para encontrar 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 entra en más detalles y corrige un caso extremo cuando las propiedades se configuran 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)">
    
  4. El enfoque más simple es siempre realizar actualizaciones importantes , ya que permite tanto nuevas instalaciones como actualizaciones en el único MSI. 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 !

  5. Crear 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/" />
    
  6. En las versiones de lanzamiento, versionamos nuestros instaladores, copiando el archivo msi en un directorio de implementación. Un ejemplo de esto usando un objetivo wixproj llamado desde el destino 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>
    
  7. Use calor para cosechar archivos con comodín (*) Guid. Útil si desea reutilizar archivos WXS en varios proyectos (consulte 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 
    

    Están sucediendo algunas cosas, robocopyes eliminar los metadatos de la copia de trabajo de Subversion antes de cosechar; la -drreferencia del directorio raíz se establece en nuestra ubicación de instalación en lugar de TARGETDIR predeterminada; -varse usa para crear una variable para especificar el directorio de origen (salida de implementación web).

  8. Una manera fácil de incluir la versión del producto en el título del cuadro de 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>
    
  9. 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 .

  10. Rob Mensching tiene una forma ordenada de localizar rápidamente los problemas en los archivos de registro de MSI mediante la búsqueda value 3. Tenga en cuenta los comentarios sobre la internacionalización.

  11. Al agregar características condicionales, es más intuitivo establecer el nivel de característica predeterminado en 0 (deshabilitado) y luego establecer el nivel de condición en el valor deseado. Si establece el nivel de característica 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 lo contrario de lo que 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>
    
revs si618
fuente
Acerca de agregar el ícono en Agregar o quitar programas, es EXACTAMENTE lo que estaba buscando. ¿Dónde pegas esas tres líneas? +1 por pura genialidad.
Everett
Tiendo a colocarlos justo después (y obviamente debajo) del elemento <Package>. Eche un vistazo al esquema de validez wix.sourceforge.net/manual-wix3/schema_index.htm
si618
+1, desearía poder hacer +100, este es el bit más útil de información de Wix con el que me he topado.
Tim Long
Gracias Tim! Rob Mensching, Bob Arson, Wim Coehen y otros merecen las felicitaciones por compartir sus conocimientos.
si618
38

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 IIS 6 Metabase Compatibility está instalado 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 &lt; 600) OR IIS_METABASE_COMPAT)
</Condition>
Simon Steele
fuente
34

Mantenga todas las ID 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í.

Creo que esto ayuda enormemente a realizar un seguimiento de todos los diversos identificadores en todas las diversas categorías.

Cheeso
fuente
No uso espacios de nombres pero agrego los ID; por ejemplo: ExampleFeature, ChmFileComponent. Supongo que me encanta escribir ;-)
dvdvorle
25

Fantástica pregunta Me encantaría ver algunas de las mejores prácticas que se muestran.

Tengo muchos archivos que distribuyo, así que configuré mi proyecto en varios archivos fuente wxs.

Tengo un archivo fuente de nivel superior 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 se componen de fragmentos que contienen grupos de componentes a los que se hace referencia en la etiqueta Feature en 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 la araña OO hormiguea un poco porque los fragmentos tienen que hacer referencia a los nombres en el archivo Product.wxs (por ejemplo, DirectoryRef), pero me resulta más fácil mantener que un solo archivo fuente grande.

¡Me encantaría escuchar comentarios sobre esto, o si alguien tiene buenos consejos también!

Peter Tate
fuente
Nuestra configuración también es muy similar a este enfoque. Es bueno porque podemos usar nuestro equivalente de Products.wxs como nuestra configuración base para una variedad de productos.
si618
@ Peter Tate: tu sentido de la araña es correcto. Vea mi respuesta sobre alias de directorio.
Wim Coenen
Tomo el mismo enfoque: Product.wxs con diseño es estático, y una tarea de compilación (heat.exe) genera mi archivo
Content.wxs
20

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 hace de esta manera, la apariencia "estándar" no es del todo correcta. La casilla de verificación es siempre un fondo gris, mientras que el cuadro de diálogo es blanco:

texto alternativo http://www.dizzymonkeydesign.com/blog/misc/adding-and-customizing-dlgs-in-wix-3/images/exit_dlg_1.gif

Una forma de evitar esto es especificar su propio ExitDialog personalizado, con una casilla de verificación ubicada de forma diferente . Esto funciona, pero parece mucho trabajo solo para cambiar el color de un control. Otra forma de resolver lo mismo es procesar posteriormente el MSI generado para cambiar los campos X, Y en la tabla 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 (desde light.exe) producirá un ExitDialog que se verá más profesional:

texto alternativo http://www.dizzymonkeydesign.com/blog/misc/adding-and-customizing-dlgs-in-wix-3/images/exit_dlg_2.gif

Cheeso
fuente
¡Decir ah! No es mi blog Yo también lo leí. Y tengo un enlace a la entrada del blog en el texto anterior. Pero lo hicieron de manera diferente que yo. ¡Me gusta más mi camino!
Cheeso el
1
Gracias por el js, muy útil! Una cosa que tuve que cambiar en el wxs es reemplazarlo WIXUI_EXITDIALOGOPTIONALCHECKBOXpor WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installeddentro<Publish>
Alexander Kojevnikov, el
¿Hay alguna manera de marcar la casilla de verificación de forma predeterminada?
Alek Davis
Para marcar la casilla de forma predeterminada, utilicé esto: <Property Id = "WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value = "1" />
Alek Davis
Parece una solución ingeniosa, pero ¿cómo la uso? ¿Hay alguna forma de poner el js dentro del elemento <AfterBuild> en mi wixproj? O ya que se refiere a ejecutarlo desde la línea de comandos, ¿es mejor como un evento posterior a la compilación, en cuyo caso, qué es un buen intérprete de línea de comandos js para Windows?
vanmelle
18

Creando versiones Live, Test, Training, ... usando los mismos archivos fuente.

En pocas palabras: cree un UpgradeCode único para cada instalador y defina automáticamente el primer carácter de cada Guid para cada instalador, dejando los 31 restantes restantes.

Prerrequisitos

Supuestos

  • Las variables de WiX se usan 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, cuadros de diálogo de IU, ...)
    • 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 al lado del botón Agregar en Visual Studio)
    • Config.wxi (tiene UpgradeCode único, ProductName, InstallName, ...)
  • Setup.Test , ...
    • según live pero Config.wxi está configurado para 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. según wixproj normal.
  • Agregue el objetivo BeforeBuild en wixproj en Setup.Live, etc. para realizar MSBuild Community Task FileUpdate para modificar Guías (utilicé A para Live, B para Test y C para entrenamiento)
  • Agregue el objetivo AfterBuild para revertir las Guías Components.wxs a 0.
  • Verifique con Orca que cada componente en cada MSI tenga el guid modificado.
  • Verifique que las guías originales se hayan restaurado.
  • 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>

Componentes de ejemplo.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 *), usando un archivo por componente y configurando el archivo como la ruta de acceso clave. Esto elimina la necesidad de llamadas ModifyComponentsGuidsy RevertComponentsGuidsobjetivos 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=&quot;([a-f]|[A-F]|\d)" ReplacementText="Guid=&quot;A" />
</Target>
<!-- Revert the first character of every Guid back to initial value -->
<Target Name="RevertComponentsGuids">
  <FileUpdate Files="..\Setup.Library\Components.wxs" Regex="Guid=&quot;([a-f]|[A-F]|\d)" ReplacementText="Guid=&quot;0" />
</Target>

Pensamientos finales

  • Este proceso también debería funcionar para crear diferentes instaladores para diferentes módulos de fusión (Live, Test, ... como características) para el mismo instalador. Fui con diferentes instaladores, ya que parecía una opción más segura, existe un mayor riesgo de que alguien actualice Live en lugar de Training si están en la misma caja y solo usa funciones para los diferentes módulos de fusión.
  • Si utiliza su MSI para realizar actualizaciones, así como nuevas instalaciones, es decir, el enfoque principal de solo actualización, 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 de directorio virtual únicos, grupos de aplicaciones, nombres de bases de datos, etc. para cada instalador.

ACTUALIZACIÓN 1: la generación automática de guías de componentes elimina la necesidad de llamar a la tarea FileUpdate si crea un componente con Guid = "*" para cada archivo, configurando el archivo como la ruta de acceso de clave.

ACTUALIZACIÓN 2: Uno de los problemas con los que nos hemos encontrado es que si no genera automáticamente sus Guid de componentes y la compilación falla, entonces los archivos temporales deben eliminarse manualmente.

ACTUALIZACIÓN 3: Se encontró una manera de eliminar la dependencia de svn: creación de archivos externos y temporales. Esto hace que el proceso de construcción sea más resistente (y es la mejor opción si no puede usar comodines para sus Guías) y menos frágil si hay un fallo de construcción en la luz o la vela.

ACTUALIZACIÓN 4: El soporte para múltiples instancias que usan transformaciones de instancias está en WiX 3.0+, definitivamente también vale la pena echarle un vistazo.

revs si618
fuente
+1 para la referencia de MSBuild Community Tasks, ama ese paquete
BozoJoe
17

Uso del registro de diagnóstico de Msi para obtener información detallada sobre fallas

msiexec /i Package.msi /l*v c:\Package.log

Dónde

Package.msi
es el nombre de su paquete y
c: \ Package.log
es donde desea la salida del registro

Códigos de error Msi

Video introductorio de Wix
Oh y el video introductorio aleatorio de Wix con "Mr. WiX" Rob Mensching es útil para el "panorama general".

Terrance
fuente
2
+1 Sería mucho mejor si pudiéramos habilitar el registro desde Wix en lugar de la línea de comando.
si618
3
WiX lo hace. Establezca la propiedad MsiLogging. Solo compatible con Windows Installer 4.0+.
Rob Mensching
Muchas gracias "Sr. Wix". Tengo que comprobar eso.
Terrance
17

Use Javascript CustomActions porque son muy fáciles

La gente ha dicho que Javascript es incorrecto para MSA CustomActions . Razones dadas: difícil de depurar, difícil de hacer confiable. No estoy de acuerdo No es difícil de depurar, ciertamente no es más difícil que C ++. Es simplemente diferente. Descubrí que escribir CustomActions en Javascript es muy fácil, mucho más fácil que usar C ++. Mucho mas rápido. E igual de confiable.

Solo hay un inconveniente: Javascript CustomActions se puede extraer a través de Orca, mientras que un CA C / C ++ requeriría ingeniería inversa. Si considera que su magia de instalador es propiedad intelectual protegida, querrá evitar el script.

Si usa script, solo necesita comenzar con alguna estructura. Aquí hay algunos para comenzar.


Código Javascript "repetitivo" 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, registre 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: utilicé Javascript para hacer una consulta WMI en IIS, para obtener una lista de sitios web existentes, en los que se podría instalar un filtro ISAPI. Esta lista se utilizó para completar un cuadro de lista que se muestra más adelante en la secuencia de la interfaz de usuario. Todo muy facil.

En IIS7, no hay un proveedor de WMI para IIS, por lo que utilicé el shell.Run()método para invocar appcmd.exe para realizar el trabajo. Fácil.

Pregunta relacionada: Acerca de Javascript CustomActions

Cheeso
fuente
2
+1 Creo que el enfoque DTF es fácil de configurar, pero javascript también podría ser útil.
si618
12

Peter Tate ya ha mostrado cómo puede definir definiciones reutilizables de ComponentGroup en fragmentos 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 su fragmento de grupo de componentes puede hablar sobre una carpeta como esta:

<DirectoryRef Id="component1InstallFolder">
...
</DirectoryRef>

Entonces el producto principal puede alias uno de sus directorios (por ejemplo, "productInstallFolder") de esta manera:

<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 un gran grupo de componentes reutilizables con un gráfico de dependencia complejo 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 extraerá 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-reusable 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 "extraído" por los archivos wxs del producto principal.

Wim Coenen
fuente
12

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 @ New Age Solutions .

Básicamente, crea una tarea personalizada de MSBuild para ejecutar una plantilla T4, y esa plantilla genera el WXS justo antes de que se compile el proyecto Wix. Esto le permite (dependiendo de cómo lo implemente) incluir automáticamente todos los resultados de los ensamblados al compilar otra solución (lo que significa que ya no tendrá que editar los wxs cada vez que agregue un nuevo ensamblaje).

Peter T. LaComb Jr.
fuente
2
+1 eso es realmente agradable, no estoy tan preocupado por los ensamblados, pero nuestros proyectos web pueden tener problemas con páginas aspx y otros artefactos (imágenes, css) que se agregan al proyecto pero no WiX.
si618
44
Para los futuros visitantes, Wix 3.5 tiene una utilidad heat.exe que realiza esta recolección automáticamente
Mrchief el
@Mrchief: no creo que Heat recoja los ensamblados referenciados que se copian localmente, aunque aparentemente está planeado para 4.0. Referencia: sourceforge.net/tracker/…
Peter T. LaComb Jr.
El calor no capta los conjuntos referenciados.
tofutim
¿Cuáles son algunos buenos ejemplos del uso de T4 para generar el archivo WXS?
tofutim
12

Usando Heat.exe para aplastar la cara e infligir "Epic Pwnage" en instalaciones dolorosamente grandes

Ampliando las respuestas de Si y Robert-P sobre el calor.

Traducción: (Uso de calor para evitar escribir archivos individuales en el proyecto a mano y para automatizar las compilaciones para un proceso general más fácil).

Descripción detallada de la sintaxis de calor de WiX 2.0

Para las versiones más nuevas (no tan diferentes de las versiones anteriores, pero hay cambios de sintaxis potencialmente molestos ...) vaya al directorio Heat está en el cmd.exe y simplemente escriba heat, pero tengo un ejemplo aquí para obtener ayuda con versiones más nuevas si es necesario.

Agregar lo siguiente a su Evento de compilación en Visual Studio 2010.
(Haga clic con el botón 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 ejecuta el comando anterior)

-scom 

No tome "archivos COM"

-sreg 

No tome "Archivos de registro"

-frag 

No agarres "Fragmentos"

-srd 

No agarres el "directorio raíz"

dir

dir indica que desea que Heat se vea en una carpeta

"$ (EnviromentVariable)"

El nombre de la variable que agregaría a las variables de preprocesador en las propiedades del proyecto (Proyecto de clic derecho, Ir a propiedades )-> Generar, donde dice Definir variables de preprocesador (se supone Visual Studio 2010)

Ejemplo:
EnviromentVariable = C: \ Project \ bin \ Debug;
No hay comillas dobles pero terminan con punto y coma

-cg GroupVariable 

El ComponentGroup al que se hará referencia desde el fragmento creado en el archivo wxs principal

FragmentoDir

El directorio de fragmentos donde se almacenará el fragmento wxs de salida

FileName.wxs

El nombre del archivo

Tutorial completo aquí, muy útil

Parte 1 Parte 2

Terrance
fuente
Hay otra herramienta útil para propósitos ligeramente diferentes: Parafina ( wintellect.com/CS/blogs/jrobbins/archive/2010/03/10/4107.aspx )
ralf.w.
9

Incluyendo objetos COM:

heatgenera la mayoría (si no todas) las entradas del registro y otra configuración necesaria para ellas. ¡Alegrarse!

Incluidos los objetos COM administrados (también conocidos como objetos .NET o C # COM)

El uso heaten un objeto COM administrado le dará un documento wix casi completo.

Si no necesita la biblioteca disponible en el GAC (es decir, disponible globalmente: la mayoría de las veces no necesita esto con sus ensamblados .NET de todos modos; probablemente haya hecho algo mal en este punto si no está destinado a ser una biblioteca compartida) querrá asegurarse de actualizar la CodeBaseclave de registro para establecerla [#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 Fileelemento: Assemblyy KeyPath. El ensamblado debe establecerse en ".net" y KeyPathdebe establecerse en "sí".

Sin embargo, algunos entornos (especialmente cualquier cosa con memoria administrada, como los lenguajes de secuencias de comandos) también necesitarán acceso a Typelib. Asegúrese de ejecutar heaten su typelib e incluirlo. heatgenerará todas las claves de registro necesarias. ¿Cuan genial es eso?

Robert P
fuente
8

Instalando a C:\ProductName

Algunas aplicaciones deben instalarse C:\ProductNameo algo similar, pero el 99.9% (si no el 100%) de los ejemplos en la instalación neta C:\Program Files\CompanyName\ProductName.

El siguiente código se puede usar para establecer la TARGETDIRpropiedad en la raíz de la C:unidad (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: ¡ Por defecto, TARGETDIR no apunta a C:\! Más bien apunta a lo ROOTDRIVEque a su vez apunta a la raíz del disco con la mayor cantidad de espacio libre ( ver aquí ), y este no es necesariamente el C:disco. ¡Puede haber otro disco duro, partición o unidad USB!

Luego, en algún lugar debajo de su <Product ...>etiqueta, 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>
gehho
fuente
¿No sería más sencillo instalarlo WindowsVolume?
Wim Coenen
1
Sí, pero tendría que usar una solución alternativa porque la WindowsVolumepropiedad no puede usarse como un Directory(el compilador da un error / advertencia), como se señala aquí y aquí . Personalmente, encuentro esta solución confusa.
gehho
7

Variables ambientales

Al compilar sus documentos Wxs en código wixobj, puede utilizar variables ambientales 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 establece justo antes de construir su MSI (ya sea con un script o manualmente, no importa) En su fuente wix, puede hacer algo como:

<define FILESOURCE = c:\source\output\bin\$(env.RELEASE_MODE) >

y luego en su código, úselo en el lugar para cambiar sobre la marcha su documento wxs, por ejemplo:

<Icon Id="myicon.ico" SourceFile="$(var.FILESOURCE)" />
Robert P
fuente
1
También están disponibles variables de compilación como $ (Configuración) y $ (Plataforma). También un montón más en msdn.microsoft.com/en-us/library/aa302186.aspx
si618
1
@Si: en algún momento antes de hoy, ese enlace ya no está activo. No pude encontrar el último.
Peter M
7

Edición de cuadros de diálogo

Una buena capacidad para editar cuadros de diálogo es utilizar 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).

user432758
fuente
Impresionante, no sabía que SharpDevelop lo apoyara.
anton.burger
6

Configuración de IIS enable32BitAppOnWin64 flag 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" />
EdmundYeung99
fuente
5

Modifique la opción "¿Listo para instalar?" diálogo (también conocido como VerifyReadyDlg) para proporcionar un resumen de las elecciones realizadas.

Se ve así:
texto alternativo http://i46.tinypic.com/s4th7t.jpg

Haga esto con una acción personalizada de Javascript:


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>

Adjunte 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 SO relacionada: ¿Cómo puedo configurar, en tiempo de ejecución, el texto que se mostrará en VerifyReadyDlg?

Cheeso
fuente
Seguramente no debería ser JScript el lenguaje de script de Windows en lugar de JavaScript el lenguaje de script DHTML. Posiblemente un poco pedante, pero podría ser un poco confuso para algunas personas.
caveman_dick
5

Poner 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 algún 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 realiza un parche, si incluye una referencia de componente único en un fragmento, todos los componentes modificados de ese fragmento aparecerá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, al aplicar parches con el tema "Uso de Purely WiX" del archivo de ayuda 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 construida utilizando los componentes en fragmentos separados. Así que asegúrese de fragmentar correctamente su producto antes de enviarlo.

Dave Andersen
fuente
5

Imprimir EULA desde Wix3.0 y posterior

1) Cuando compila su código fuente wix, el light.exe debe hacer referencia a WixUIExtension.dll en la línea de comandos. Use el modificador de línea de comando -ext para esto.

2) Si al agregar la referencia a WixUIExtension.dll, su proyecto no se compila, probablemente se deba a conflictos de ID de diálogo, es decir, su proyecto estaba usando las mismas ID de diálogo que algunos diálogos estándar en WixUIExtension.dll, dar diferentes ID a sus cuadros de diálogo. Este es un problema bastante común.

3) Su diálogo de licencia debe tener control ScrollableText con la identificación "LicenseText". Wix busca exactamente este nombre de control cuando 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 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 entre mayúsculas y minúsculas).

Cuando el usuario presiona el botón, se le presentará el cuadro de diálogo Seleccionar impresora estándar y podrá imprimir desde allí.

farfareast
fuente
5
  • Mostramos la versión del producto en algún lugar (pequeño) en la primera pantalla de la GUI. Porque las personas tienden a cometer errores al elegir la versión correcta cada vez. (Y mantengan a los desarrolladores buscando edades ...)

  • Hemos configurado TFSBuild para generar también transformaciones (archivos .mst) con la configuración para nuestros diferentes entornos. (Conocemos todos los entornos en los que debemos implementar).

Dado que la publicación original del blog de Grant Holliday no funciona, copié y pegué su contenido aquí:


Tarea 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 los ajustes de configuración específicos 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:

  1. Son un formato binario
  2. 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 "bases de datos" de MSI y crear archivos de transformación.

Los créditos vuelven a Alex Shevchuk - De MSI a WiX - Parte 7 - Personalización de la instalación usando Transformaciones 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 MSBuild. La tarea de MSBuild

Descargue el código fuente y el ejemplo transforms.xml aquí (~ 7Kb Zipped VS2008 Solution)


thijs
fuente
2
Redefinimos WelcomeDlgTitle en mi archivo de localizaciones. ¡Funciona muy bien! <String Id = "WelcomeDlgTitle"> {\ WixUI_Font_Bigger} Bienvenido al Asistente de configuración [ProductName] [ProductVersion] </String>
saschabeaumont
5

Antes de implementar un paquete de instalación, siempre controlo el contenido del mismo.

Es solo una simple llamada en la línea de comando (según la publicación de Terrences) abra la línea de comando e ingrese

msiexec /a Package.msi /qb TARGETDIR="%CD%\Extract" /l*vx "%CD\install.log%"

Esto extraerá el contenido del paquete a un subdirectorio 'Extraer' con la ruta actual.

tobaer
fuente
4

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.

usuario432758
fuente
4

Registro de ensamblados .NET para COM Interop con compatibilidad x86 / x64

Nota: este fragmento es esencialmente el mismo que REGASM Assembly.dll / codebase

Están sucediendo un par de cosas en este ejemplo, así que aquí está el código y lo explicaré después ...

  <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 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 trata con la compatibilidad x86 vs x64. Mi ensamblaje apunta a 'Cualquier CPU', por lo 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 . If-then-else me prepara para esto, los valores se usan en un foreachbucle más adelante. De esta manera, solo tengo que crear las claves de registro una vez (principio DRY).

El elemento de archivo especifica el dll de ensamblado 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 Assembly=".net"tenga en cuenta que , este atributo solo haría que la asamblea se coloque en el GAC, que NO es lo que quería. Usar el AssemblyApplicationatributo para volver a apuntar a sí mismo es simplemente una forma de evitar que Wix coloque el archivo en el GAC. Ahora que Wix sabe que es un ensamblado .net, sin embargo, me permite usar ciertas variables de carpeta dentro de mi XML, como el !(bind.assemblyFullname.filDriverAssembly)para obtener el nombre completo del ensamblado.

Tim Long
fuente
3

Establezca la DISABLEADVTSHORTCUTSpropiedad 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 utilizarla como ruta de acceso de teclado.

<Property Id="DISABLEADVTSHORTCUTS" Value="1"/>

Creo que Windows Installer 4.0 o superior es un requisito .

Dave Andersen
fuente
2

Es una estructura agradable pero, según mi experiencia, me pregunto cómo abordas estas condiciones:

R. Sus instalaciones parecen aterrizar 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 PRUEBA y / o ENTRENAMIENTO pero que aún no están en VIVO?


fuente
Hola Blaine, A. No, no lo hacen. InstallName está en Config.wxi, que es el único archivo al que no hace referencia svn: externals. Esto es único para cada instalación, es decir, para cada producto. Por eso también modificamos los Guías para cada versión. B. GOTO A. :) Son MSI independientes con su propio UpgradeCode.
si618
1
Por cierto, entiendo por qué respondiste mi pregunta con una pregunta, pero una vez que obtengas suficientes puntos de repetición, mueve tu pregunta a los comentarios de respuesta, de lo contrario, el hilo será difícil de seguir.
si618
2

Aquí hay una manera de ayudar a los grandes proyectos web a verificar que la cantidad de archivos implementados coincida con la cantidad de archivos integrados en un MSI (o módulo de fusión). Acabo de ejecutar la tarea personalizada MSBuild contra nuestra aplicación principal (todavía en desarrollo) y recogió bastantes archivos faltantes, en su mayoría imágenes, ¡pero algunos archivos javascript se deslizaron hasta!

Este enfoque (echar un vistazo a la tabla de archivos de MSI al conectarse al destino AfterBuild del proyecto WiX) podría funcionar para otros tipos de aplicaciones en las que tiene acceso a una lista completa de archivos esperados.

si618
fuente
2

Realizar una reinstalación forzada cuando una instalación no permite la desinstalación o reinstalación y no se revierte.

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)
Terrance
fuente
2

Cree una IU que tenga una acción personalizada que establecerá una variable y la UI deshabilitará / habilitará el siguiente botón (o similar) según 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 ningún lado!

Interacciones de Wix con condiciones, propiedades y acciones personalizadas

Daniel Powell
fuente