Trucs et astuces WiX


264

Nous utilisons WiX depuis un certain temps maintenant, et malgré les reproches habituels concernant la facilité d'utilisation, cela se passe assez bien. Ce que je recherche, ce sont des conseils utiles concernant:

  • Configuration d'un projet WiX (mise en page, références, modèles de fichiers)
  • Intégration de WiX dans les solutions et processus de création / publication
  • Configuration des programmes d'installation pour de nouvelles installations et mises à niveau
  • Tous les bons hacks WiX que vous souhaitez partager

jetez un oeil à gui4wix.codeplex.com
TarunG

10
Fermé comme pas constructif? J'ai appris des tas en posant cette question! Un peu de cohérence de StackOverflow serait également bien ... par exemple stackoverflow.com/questions/550632/…
si618

15
Il a obtenu '203' Ups, cela suffit pour prouver son utilité.
TarunG

Les questions SO doivent avoir une réponse définitive et correcte; les questions ouvertes font que les questions que les gens posent sur les problèmes réels disparaissent de la première page. faq @Si.: Cette politique a toujours été là AFAIK, mais elle est mieux appliquée maintenant; cette question a presque trois ans.
Jim Dagg

Assez juste Jim, c'est une question ouverte, et je suppose que c'est à la communauté SO de décider, mais je dois dire que la fermer comme non constructive semble étrange, étant donné que moi et à en juger par beaucoup d'autres personnes ont trouvé cette question utile (par exemple goo.gl/Zqp2X ), et qu'elle correspond très bien à la practical, answerable questions based on actual problems that you facepartie de la FAQ.
si618

Réponses:


157
  1. Conservez les variables dans un wxifichier include distinct . Permet la réutilisation, les variables sont plus rapides à trouver et (si nécessaire) permet une manipulation plus facile par un outil externe.

  2. Définir les variables de plate-forme pour les versions x86 et 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. Stockez l'emplacement d'installation dans le registre, permettant aux mises à niveau de trouver l'emplacement correct. Par exemple, si un utilisateur définit un répertoire d'installation personnalisé.

     <Property Id="INSTALLLOCATION">
        <RegistrySearch Id="RegistrySearch" Type="raw" Root="HKLM" Win64="$(var.Win64)"
                  Key="Software\Company\Product" Name="InstallLocation" />
     </Property>

    Remarque : le gourou de WiX, Rob Mensching, a publié un excellent article de blog qui va plus en détail et corrige un cas de bord lorsque les propriétés sont définies à partir de la ligne de commande.

    Exemples utilisant 1. 2. et 3.

    <?include $(sys.CURRENTDIR)\Config.wxi?>
    <Product ... >
      <Package InstallerVersion="200" InstallPrivileges="elevated"
               InstallScope="perMachine" Platform="$(var.Platform)"
               Compressed="yes" Description="$(var.ProductName)" />

    et

    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="$(var.PlatformProgramFilesFolder)">
        <Directory Id="INSTALLLOCATION" Name="$(var.InstallName)">
  4. L'approche la plus simple consiste toujours à effectuer des mises à niveau majeures , car elle permet à la fois de nouvelles installations et des mises à niveau dans le même MSI. UpgradeCode est fixé sur un Guid unique et ne changera jamais, sauf si nous ne voulons pas mettre à niveau le produit existant.

    Remarque : dans WiX 3.5, il y a un nouvel élément MajorUpgrade qui rend la vie encore plus facile !

  5. Création d'une icône dans Ajout / Suppression de programmes

    <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. Sur les versions, nous mettons à jour nos programmes d'installation, en copiant le fichier msi dans un répertoire de déploiement. Un exemple de cela en utilisant une cible wixproj appelée depuis la cible 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. Utilisez la chaleur pour récolter les fichiers avec le caractère générique (*) Guid. Utile si vous souhaitez réutiliser des fichiers WXS sur plusieurs projets (voir ma réponse sur plusieurs versions du même produit). Par exemple, ce fichier de commandes récolte automatiquement la sortie 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 

    Il se passe un peu de temps, robocopyen supprimant les métadonnées de la copie de travail de Subversion avant la récolte; la -drréférence du répertoire racine est définie sur notre emplacement d'installation plutôt que sur TARGETDIR par défaut; -varest utilisé pour créer une variable pour spécifier le répertoire source (sortie de déploiement Web).

  8. Un moyen facile d'inclure la version du produit dans le titre de la boîte de dialogue de bienvenue en utilisant Strings.wxl pour la localisation. (Crédit: saschabeaumont . Ajouté car ce bon conseil est caché dans un commentaire)

    <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. Épargnez-vous de la douleur et suivez les conseils de Wim Coehen d'un composant par fichier. Cela vous permet également de laisser de côté (ou caractère générique *) le GUID du composant .

  10. Rob Mensching a une manière ordonnée de dépister rapidement des problèmes dans les fichiers journaux MSI en recherchant value 3. Notez les commentaires concernant l'internationalisation.

  11. Lors de l'ajout de fonctionnalités conditionnelles, il est plus intuitif de définir le niveau de fonctionnalité par défaut sur 0 (désactivé), puis de définir le niveau de condition à la valeur souhaitée. Si vous définissez le niveau de fonctionnalité par défaut> = 1, le niveau de condition doit être 0 pour le désactiver, ce qui signifie que la logique de condition doit être l'opposé de ce que vous attendez, ce qui peut être déroutant :)

    <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>

À propos de l'ajout de l'icône dans Ajout / Suppression de programmes, c'est exactement ce que je cherchais. Où collez-vous ces trois lignes? +1 pour la pure impression.
Everett

J'ai tendance à les placer juste après (et évidemment en dessous) l'élément <Package>. Jetez un œil au schéma de validité wix.sourceforge.net/manual-wix3/schema_index.htm
si618

+1, j'aimerais pouvoir faire +100, c'est le bit d'information Wix le plus utile sur lequel je suis tombé.
Tim Long

Merci Tim! Rob Mensching, Bob Arson, Wim Coehen et d'autres méritent les félicitations pour partager leurs connaissances.
si618

38

Vérification si IIS est installé:

<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>

Vérification de la compatibilité de la métabase IIS 6 sur 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>

34

Conserver tous les ID dans des espaces de noms séparés

  • Les fonctionnalités commencent par des F. exemples: F.Documentation, F.Binaries, F.SampleCode.
  • Les composants commencent par C. Ex: C.ChmFile, C.ReleaseNotes, C.LicenseFile, C.IniFile, C.Registry
  • Les actions personnalisées sont CA. Ex: CA.LaunchHelp, CA.UpdateReadyDlg, CA.SetPropertyX
  • Les fichiers sont Fi.
  • Les répertoires sont Di.
  • etc.

Je trouve que cela aide énormément à garder une trace de tous les différents identifiants dans toutes les différentes catégories.


Je n'utilise pas d'espaces de noms mais j'ajoute les identifiants; par exemple: ExamplesFeature, ChmFileComponent. Je suppose que j'aime taper ;-)
dvdvorle

25

Question fantastique. J'adorerais voir quelques bonnes pratiques présentées.

J'ai beaucoup de fichiers que je distribue, j'ai donc configuré mon projet dans plusieurs fichiers source wxs.

J'ai un fichier source de premier niveau que j'appelle Product.wxs qui contient essentiellement la structure de l'installation, mais pas les composants réels. Ce fichier comporte plusieurs sections:

<Product ...>
  <Package ...>
    <Media>... 
   <Condition>s ...
   <Upgrade ..>
   <Directory> 
        ...
   </Directory>
   <Feature>
      <ComponentGroupRef ... > A bunch of these that
   </Feature>
   <UI ...>
   <Property...>
   <Custom Actions...>
   <Install Sequences....
  </Package>
</Product>

Les autres fichiers .wix sont composés de fragments contenant des groupes de composants référencés dans la balise Feature du Product.wxs. Mon projet contient un joli regroupement logique des fichiers que je distribue

<Fragment>
   <ComponentGroup>
     <ComponentRef>
     ....
    </ComponentGroup>
    <DirectoryRef>
      <Component... for each file
      .... 
    </DirectoryRef>
</Fragment>

Ce n'est pas parfait, mon sens d'araignée OO pique un peu parce que les fragments doivent faire référence à des noms dans le fichier Product.wxs (par exemple, DirectoryRef), mais je trouve plus facile de conserver un seul gros fichier source.

J'adorerais entendre des commentaires à ce sujet, ou si quelqu'un a aussi de bons conseils!


Notre configuration est également très similaire à cette approche. C'est bien parce que nous pouvons utiliser notre équivalent de Products.wxs comme configuration de base pour une variété de produits.
si618

@Peter Tate: votre sens de l'araignée est correct. Voir ma réponse sur l'alias de répertoire.
Wim Coenen

J'adopte la même approche: Product.wxs avec mise en page est statique et une tâche de génération (heat.exe) génère mon fichier
Content.wxs

20

Ajoutez une case à cocher dans la boîte de dialogue de sortie pour lancer l'application ou le fichier d'aide.

...

<!-- 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 vous le faites de cette façon, l'apparence "standard" n'est pas tout à fait correcte. La case à cocher est toujours un arrière-plan gris, tandis que la boîte de dialogue est blanche:

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

Une solution consiste à spécifier votre propre ExitDialog personnalisé, avec une case à cocher située différemment . Cela fonctionne, mais semble beaucoup de travail juste pour changer la couleur d'un contrôle. Une autre façon de résoudre le même problème consiste à post-traiter le MSI généré pour modifier les champs X, Y dans la table de contrôle pour ce contrôle CheckBox particulier. Le code javascript ressemble à ceci:

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();

L'exécution de ce code en tant que script de ligne de commande (à l'aide de cscript.exe) après la génération du MSI (à partir de light.exe) produira un ExitDialog qui semble plus professionnel:

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


Ha! Pas mon blog. Je l'ai lu aussi. Et j'ai un lien vers l'entrée de blog dans le texte ci-dessus. Mais ils l'ont fait différemment que moi. J'aime mieux ma façon. !!
Cheeso

1
Merci pour le js, très utile! Une chose que j'ai dû changer dans les wx est de remplacer WIXUI_EXITDIALOGOPTIONALCHECKBOXpar l' WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installedintérieur<Publish>
Alexander Kojevnikov

Existe-t-il un moyen de cocher la case par défaut?
Alek Davis

Pour cocher la case par défaut, j'ai utilisé ceci: <Property Id = "WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value = "1" />
Alek Davis

Cela semble être une solution astucieuse, mais comment puis-je l'utiliser? Existe-t-il un moyen de mettre les js à l'intérieur de l'élément <AfterBuild> dans mon wixproj? Ou puisque vous faites référence à son exécution à partir de la ligne de commande, est-il préférable en tant qu'événement post-génération, auquel cas, qu'est-ce qu'un bon interpréteur de ligne de commande js pour Windows?
vanmelle

18

Création de versions Live, Test, Training, ... en utilisant les mêmes fichiers sources.

En bref: créez un code de mise à niveau unique pour chaque programme d'installation et définissez automatiquement le premier caractère de chaque GUID pour chaque programme d'installation, en laissant les 31 restants uniques.

Conditions préalables

Hypothèses

  • Les variables WiX sont utilisées pour définir UpgradeCode, ProductName, InstallName.
  • Vous avez déjà un installateur fonctionnel. Je n'essaierais pas ceci avant que vous le fassiez.
  • Tous vos composants sont conservés dans un seul fichier (Components.wxs). Ce processus fonctionnera si vous avez plusieurs fichiers, il y aura juste plus de travail à faire.

Structure du répertoire

  • Setup.Library
    • Tous les fichiers wxs (Composants, Fonctionnalités, Dialogues UI, ...)
    • Common.Config.wxi (ProductCode = "*", ProductVersion, PlatformProgramFilesFolder, ...)
  • Setup.Live (wixproj)
    • Liez tous les fichiers Setup.Library à l'aide de "Ajouter un fichier existant" -> "Ajouter en tant que lien" (le petit bouton fléché vers le bas juste à côté du bouton Ajouter dans Visual Studio)
    • Config.wxi (possède un UpgradeCode, ProductName, InstallName unique, ...)
  • Setup.Test , ...
    • selon live mais Config.wxi est configuré pour l'environnement de test.

Processus

  • Créez le répertoire Setup.Library et déplacez tous vos fichiers wxs et wxi (sauf Config.wxi) du projet existant.
  • Créez Setup.Live, Setup.Test, etc. selon le wixproj normal.
  • Ajoutez la cible BeforeBuild dans wixproj dans Setup.Live, etc. pour effectuer la mise à jour du fichier de tâches de communauté MSBuild pour modifier les guides (j'ai utilisé A pour Live, B pour Test et C pour la formation)
  • Ajoutez la cible AfterBuild pour rétablir les composants.wxs à 0.
  • Vérifiez auprès d'Orca que chaque composant de chaque MSI a le guide modifié.
  • Vérifiez que les guides d'origine sont restaurés.
  • Vérifiez que chaque MSI installe (et met à niveau) le produit et l'emplacement corrects.

Exemple 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>

Exemple 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>

Exemple 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">
          ...

Remarque: je suggère maintenant de laisser l'attribut Guid hors du composant (équivalent de *), d'utiliser un fichier par composant et de définir le fichier comme chemin d'accès. Cela supprime le besoin d'appeler ModifyComponentsGuidset les RevertComponentsGuidscibles indiquées ci-dessous. Cependant, cela pourrait ne pas être possible pour tous vos composants.

Exemple 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>

Dernières pensées

  • Ce processus devrait également fonctionner pour créer différents programmes d'installation pour différents modules de fusion (Live, Test, ... en tant que fonctionnalités) pour le même programme d'installation. Je suis allé avec différents installateurs car cela semblait une option plus sûre, il y a plus de risques que quelqu'un mette à niveau Live au lieu de Training s'il est sur la même boîte et que vous utilisez simplement des fonctionnalités pour les différents modules de fusion.
  • Si vous utilisez votre MSI pour effectuer des mises à niveau ainsi que de nouvelles installations, c'est-à-dire l'approche de mise à niveau principale uniquement, et que vous enregistrez votre emplacement d'installation dans le registre, n'oubliez pas de créer une variable pour le nom de clé pour chaque installation.
  • Nous créons également des variables dans chaque Config.wxi pour activer des noms de répertoires virtuels uniques, des pools d'applications, des noms de bases de données, etc. pour chaque programme d'installation.

MISE À JOUR 1: Composant de génération automatique Guids supprime la nécessité d'appeler la tâche FileUpdate si vous créez un composant avec Guid = "*" pour chaque fichier, en définissant le fichier comme chemin d'accès.

MISE À JOUR 2: L'un des problèmes auxquels nous nous heurtons est que si vous ne générez pas automatiquement vos composants Guid et que la construction échoue, les fichiers temporaires doivent être supprimés manuellement.

MISE À JOUR 3: a trouvé un moyen de supprimer la dépendance à svn: externals et la création de fichiers temporaires. Cela rend le processus de construction plus résilient (et est la meilleure option si vous ne pouvez pas utiliser de caractères génériques pour vos guides) et moins fragile s'il y a un échec de construction en lumière ou en bougie.

MISE À JOUR 4: La prise en charge de plusieurs instances à l' aide de transformations d'instance est dans WiX 3.0+, vaut également le coup d'œil.


+1 pour la référence MSBuild Community Tasks,
adorez

17

Utilisation de la journalisation de diagnostic Msi pour obtenir des informations détaillées sur les échecs

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

Package.msi
est le nom de votre colis et
c: \ Package.log
est l'endroit où vous voulez la sortie du journal

Codes d'erreur Msi

Vidéo d'introduction de Wix
Oh et vidéo d'introduction de Wix aléatoire mettant en vedette "M. WiX" Rob Mensching est "une grande image conceptuelle" utile.


2
+1 Il serait beaucoup mieux si nous pouvions activer la journalisation à partir de Wix au lieu de la ligne de commande.
si618

3
WiX le fait. Définissez la propriété MsiLogging. Uniquement pris en charge par Windows Installer 4.0+.
Rob Mensching

Merci beaucoup "M. Wix". Je dois vérifier ça.
Terrance

17

Utilisez Javascript CustomActions parce qu'elles sont tellement faciles

Les gens ont dit que Javascript n'est pas la bonne chose à utiliser pour les actions personnalisées MSI . Raisons invoquées: difficile à déboguer, difficile à rendre fiable. Je ne suis pas d'accord. Ce n'est pas difficile à déboguer, certainement pas plus difficile que C ++. C'est juste différent. J'ai trouvé que l'écriture de CustomActions en Javascript était super facile, beaucoup plus facile que d'utiliser C ++. Plus vite. Et tout aussi fiable.

Il n'y a qu'un seul inconvénient: les actions personnalisées Javascript peuvent être extraites via Orca, tandis qu'une autorité de certification C / C ++ nécessiterait une ingénierie inverse. Si vous considérez que votre magie d'installation est une propriété intellectuelle protégée, vous voudrez éviter le script.

Si vous utilisez un script, il vous suffit de commencer par une structure. En voici quelques-uns pour vous aider à démarrer.


Code Javascript "passe-partout" pour 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
    };
}

Ensuite, enregistrez l'action personnalisée avec quelque chose comme ceci:

<Fragment>
  <Binary Id="IisScript_CA" SourceFile="CustomActions.js" />

  <CustomAction Id="CA.MyCustomAction"
              BinaryKey="IisScript_CA"
              JScriptCall="MyCustomActionInJavascript_CA"
              Execute="immediate"
              Return="check" />
</Fragmemt>

Vous pouvez bien sûr insérer autant de fonctions Javascript que vous le souhaitez, pour plusieurs actions personnalisées. Un exemple: j'ai utilisé Javascript pour faire une requête WMI sur IIS, pour obtenir une liste des sites Web existants, sur lesquels un filtre ISAPI pourrait être installé. Cette liste a ensuite été utilisée pour remplir une zone de liste affichée plus loin dans la séquence d'interface utilisateur. Tout est très simple.

Sur IIS7, il n'y a pas de fournisseur WMI pour IIS, j'ai donc utilisé l' shell.Run()approche pour appeler appcmd.exe pour effectuer le travail. Facile.

Question connexe: À propos des actions personnalisées Javascript


2
+1 Je trouve l'approche DTF facile à configurer, mais javascript pourrait aussi être utile.
si618

12

Peter Tate a déjà montré comment vous pouvez définir des définitions de ComponentGroup réutilisables dans des fragments wix séparés. Quelques astuces supplémentaires liées à cela:

Alias ​​de répertoire

Les fragments de groupe de composants n'ont pas besoin de connaître les répertoires définis par les wxs du produit principal. Dans votre fragment de groupe de composants, vous pouvez parler d'un dossier comme celui-ci:

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

Le produit principal peut alors alias un de ses répertoires (par exemple "productInstallFolder") comme ceci:

<Directory Id="productInstallFolder" Name="ProductName">
   <!-- not subfolders (because no Name attribute) but aliases for parent! -->
   <Directory Id="component1InstallFolder"/> 
   <Directory Id="component2InstallFolder"/> 
</Directory>

Graphique de dépendance

Les éléments ComponentGroup peuvent contenir des éléments enfants ComponentGroupRef. C'est très bien si vous avez un grand pool de composants réutilisables avec un graphique de dépendance complexe entre eux. Vous venez de configurer un ComponentGroup dans son propre fragment pour chaque composant et déclarez les dépendances comme ceci:

<ComponentGroup Id="B">
   <ComponentRef Id="_B" />
   <ComponentGroupRef Id="A">
</ComponentGroup>

Si vous référencez maintenant le groupe de composants "B" dans votre configuration car il s'agit d'une dépendance directe de votre application, il récupérera automatiquement le groupe de composants "A" même si l'auteur de l'application ne s'est jamais rendu compte qu'il s'agissait d'une dépendance de "B". Cela "fonctionne" aussi longtemps que vous n'avez pas de dépendances circulaires.

Wixlib réutilisable

L'idée de graphique de dépendance ci-dessus fonctionne mieux si vous compilez les composants big-pool-o-reusable-en un wixlib réutilisable avec lit.exe. Lors de la création d'une configuration d'application, vous pouvez référencer ce wixlib un peu comme un fichier wixobj. L'éditeur de liens candle.exe éliminera automatiquement tous les fragments qui ne sont pas "extraits" par le ou les fichiers wxs du produit principal.


12

Je suis surpris que personne n'ait mentionné l'utilisation de T4 pour générer le fichier WXS lors de la construction. J'ai appris cela grâce à Henry Lee @ New Age Solutions .

Essentiellement, vous créez une tâche MSBuild personnalisée pour exécuter un modèle T4, et ce modèle génère le WXS juste avant la compilation du projet Wix. Cela vous permet (selon la façon dont vous l'implémentez) d'inclure automatiquement tous les assemblys issus de la compilation d'une autre solution (ce qui signifie que vous n'avez plus à modifier les wx à chaque fois que vous ajoutez un nouvel assemblage).


2
+1 c'est vraiment sympa, je ne m'inquiète pas tellement pour les assemblages, mais nos projets web peuvent avoir des problèmes avec les pages aspx et autres artefacts (images, css) qui sont ajoutés au projet mais pas WiX.
si618

4
Pour les futurs visiteurs, Wix 3.5 a un utilitaire heat.exe qui effectue cette récolte automatiquement
Mrchief

@Mrchief - Je ne crois pas que Heat récupère les assemblys référencés qui sont copiés localement - cela est cependant apparemment prévu pour 4.0. Référence: sourceforge.net/tracker/…
Peter T. LaComb Jr.

La chaleur ne ramasse pas les assemblages référencés.
tofutim

Quels sont les bons exemples d'utilisation de T4 pour générer le fichier WXS?
tofutim

12

Utilisation de Heat.exe pour briser le visage et infliger "Epic Pwnage" sur des installations douloureusement grandes

Développer les réponses de Si et Robert-P sur la chaleur.

Traduction: (Utilisation de la chaleur pour éviter de taper des fichiers individuels dans le projet à la main et pour automatiser les builds pour un processus global plus facile.)

Syntaxe de la chaleur WiX 2.0 détaillée

Pour les versions plus récentes (pas si différentes des anciennes versions mais il y a des changements de syntaxe potentiellement ennuyeux ....) allez dans le répertoire Heat is in from cmd.exe et tapez simplement heat mais j'ai un exemple ici pour de l'aide avec des versions plus récentes si nécessaire.

Ajout des éléments suivants à votre événement de
génération dans Visual Studio 2010. (Cliquez avec le bouton droit sur Projet-> Propriétés -> Événements de génération-> Événements de pré-génération)

$(WIX)bin\heat.exe" dir "$(EnviromentVariable)" -cg GroupVariable -gg -scom -sreg -sfrag - srd -dr INSTALLLOCATION -var env.LogicPath -out "$(FragmentDir)\FileName.wxs

-gg 

Génère des guides lorsque la chaleur est exécutée (comme lorsque vous exécutez la commande ci-dessus)

-scom 

Ne saisissez pas les "fichiers COM"

-sreg 

Ne saisissez pas les "fichiers de registre"

-sfrag 

Ne saisissez pas les «fragments»

-srd 

Ne saisissez pas le "root root"

dir

dir indique que Heat doit rechercher dans un dossier

"$ (EnviromentVariable)"

Le nom de la variable que vous ajouteriez aux variables du préprocesseur dans la section Propriétés du projet (Projet avec le bouton droit de la souris, Aller aux propriétés) -> Créer où il est indiqué Définir les variables du préprocesseur (en supposant que Visual Studio 2010)

Exemple:
EnviromentVariable = C: \ Project \ bin \ Debug;
Pas de guillemets doubles mais se termine par un point-virgule

-cg GroupVariable 

Le ComponentGroup qui sera référencé à partir du fragment créé dans le fichier wxs principal

FragmentDir

Le répertoire de fragments où le fragment wxs de sortie sera stocké

FileName.wxs

Le nom du fichier

Tutoriel complet ici, donc freakin utile

Partie 1 Partie 2


Il existe un autre outil utile à des fins légèrement différentes: la paraffine ( wintellect.com/CS/blogs/jrobbins/archive/2010/03/10/4107.aspx )
ralf.w.

9

Y compris les objets COM:

heatgénère la plupart (sinon la totalité) des entrées de registre et des autres configurations nécessaires pour celles-ci. Réjouir!

Y compris les objets COM gérés (également appelés objets COM .NET ou C #)

L'utilisation heatsur un objet COM géré vous donnera un document Wix presque complet.

Si vous n'avez pas besoin de la bibliothèque disponible dans le GAC (c'est-à-dire, disponible globalement: la plupart du temps, vous n'en avez pas besoin avec vos assemblys .NET de toute façon - vous avez probablement fait quelque chose de mal à ce stade si elle n'est pas destinée à être une bibliothèque partagée), vous devez vous assurer de mettre à jour la CodeBaseclé de registre à définir [#ComponentName]. Si vous prévoyez de l'installer sur le GAC (par exemple, vous avez créé une nouvelle bibliothèque commune impressionnante que tout le monde voudra utiliser), vous devez supprimer cette entrée et ajouter deux nouveaux attributs à l' Fileélément: Assemblyet KeyPath. L'assembly doit être défini sur ".net" et KeyPathdoit être défini sur "oui".

Cependant, certains environnements (en particulier tout ce qui a de la mémoire gérée comme les langages de script) auront également besoin d'accéder à Typelib. Assurez-vous de lancer heatvotre typelib et de l'inclure. heatgénérera toutes les clés de registre nécessaires. À quel point cela est cool?


8

Installation sur C:\ProductName

Certaines applications doivent être installées sur C:\ProductNameou quelque chose de similaire, mais 99,9% (sinon 100%) des exemples de l'installation nette sur C:\Program Files\CompanyName\ProductName.

Le code suivant peut être utilisé pour définir la TARGETDIRpropriété à la racine du C:lecteur (extrait de la liste des utilisateurs 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>

REMARQUE: Par défaut, TARGETDIR ne pointe pas vers C:\! Il pointe plutôt vers ROOTDRIVElequel pointe à son tour vers la racine du lecteur avec le plus d'espace libre ( voir ici ) - et ce n'est pas nécessairement le C:lecteur. Il peut y avoir un autre disque dur, une partition ou un lecteur USB!

Ensuite, quelque part sous votre <Product ...>balise, vous avez besoin des balises de répertoire suivantes comme d'habitude:

<Directory Id="TARGETDIR" Name="SourceDir">
    <Directory Id="APPLICATIONFOLDER" Name="$(var.ProductName)">
        <!-- your content goes here... -->
    </Directory>
</Directory>

Ne serait-il pas plus simple d'installer simplement WindowsVolume?
Wim Coenen

1
Oui, mais vous devrez utiliser une solution de contournement car la WindowsVolumepropriété ne peut pas être utilisée en tant que Directory(le compilateur donne une erreur / un avertissement), comme indiqué ici et ici . Personnellement, je trouve cette solution déroutante.
gehho

7

Variables environnementales

Lors de la compilation de vos documents Wxs en code wixobj, vous pouvez utiliser des variables d'environnement pour déterminer diverses informations. Par exemple, supposons que vous souhaitiez modifier les fichiers à inclure dans un projet. Disons que vous avez une variable d'environnement appelée RELEASE_MODE, que vous définissez juste avant de construire votre MSI (soit avec un script, soit manuellement, cela n'a pas d'importance) Dans votre source wix, vous pouvez faire quelque chose comme:

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

puis plus tard dans votre code, utilisez-le en place pour changer à la volée votre document wxs, par exemple:

<Icon Id="myicon.ico" SourceFile="$(var.FILESOURCE)" />

1
Des variables de compilation telles que $ (Configuration) et $ (Platform) sont également disponibles. Aussi beaucoup plus sur msdn.microsoft.com/en-us/library/aa302186.aspx
si618

1
@Si - Quelque temps avant aujourd'hui, ce lien n'est plus actif. Je n'ai pas pu trouver le dernier.
Peter M



7

Modification des boîtes de dialogue

Une bonne capacité à modifier les boîtes de dialogue est d'utiliser SharpDevelop dans une version 4.0.1.7090 (ou supérieure). Avec l'aide de cet outil, une boîte de dialogue autonome (fichiers wxs provenant de sources WiX comme par exemple InstallDirDlg.wxs) peut être ouverte, prévisualisée et modifiée en mode Création.


Génial, je ne savais pas que SharpDevelop le supportait.
anton.burger

6

Définition de l'indicateur 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" />

5

Modifiez le "Prêt à installer?" (aka VerifyReadyDlg) pour fournir un résumé des choix effectués.

Il ressemble à ceci:
texte alternatif http://i46.tinypic.com/s4th7t.jpg

Faites-le avec une action personnalisée Javascript:


Code 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);
}

Déclarez le 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>

Attachez l'AC à un bouton. Dans cet exemple, l'autorité de certification est déclenchée lorsque vous cliquez sur Suivant dans CustomizeDlg:

<UI ...>
  <Publish Dialog="CustomizeDlg" Control="Next" Event="DoAction" 
           Value="CA.UpdateReadyDialog" Order="1"/>
</UI>

Question SO connexe: Comment puis-je définir, lors de l'exécution, le texte à afficher dans VerifyReadyDlg?


Cela ne devrait certainement pas être JScript le langage de script Windows plutôt que JavaScript le langage de script DHTML. Peut-être un peu pédant, mais cela pourrait être un peu déroutant pour certaines personnes.
caveman_dick

5

Mettez des composants qui peuvent être corrigés individuellement dans leurs propres fragments

Il en va de même pour les programmes d'installation de produits et les correctifs que si vous incluez un composant dans un fragment, vous devez inclure tous les composants dans ce fragment. Dans le cas de la construction d'un programme d'installation, si vous manquez des références de composants, vous obtiendrez une erreur de liaison de light.exe. Cependant, lorsque vous créez un correctif, si vous incluez une référence de composant unique dans un fragment, tous les composants modifiés de ce fragment s'afficheront dans votre correctif.

comme ça:

<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>

au lieu de cela:

<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>

En outre, lors de l'application de correctifs à l'aide de la rubrique «Utilisation de purement WiX» du fichier d'aide WiX.chm, utilisez cette procédure pour générer le correctif:

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

il ne suffit pas de simplement construire la version 1.1 du product.wixpdb en utilisant les composants dans des fragments séparés. Assurez-vous donc de fragmenter correctement votre produit avant l'expédition.


5

Impression du CLUF à partir de Wix3.0 et versions ultérieures

1) Lorsque vous compilez votre code source wix, light.exe doit référencer le WixUIExtension.dll en ligne de commande. Utilisez le commutateur de ligne de commande -ext pour cela.

2) Si, lorsque vous ajoutez la référence à WixUIExtension.dll, votre projet ne parvient pas à se compiler, cela est probablement dû à des conflits d'ID de boîte de dialogue, c'est-à-dire que votre projet utilisait les mêmes ID de boîte de dialogue que certaines boîtes de dialogue standard dans WixUIExtension.dll, donnez des identifiants différents à vos dialogues. C'est un problème assez courant.

3) Votre boîte de dialogue de licence doit avoir le contrôle ScrollableText avec l'ID "LicenseText". Wix recherche exactement ce nom de contrôle lors de l'impression.

<Control Id="LicenseText" Type="ScrollableText" X="20" Y="60" Width="330" Height="160" Sunken="yes" TabSkip="no">
    <Text SourceFile="License.rtf" />
</Control>

et un PushButton qui fait référence à l'action personnalisée

<Control Type="PushButton" Id="PrintButton" Width="57" Height="17" X="19" Y="244" Text="Print">
    <Publish Event="DoAction" Value="PrintEula">1</Publish>
</Control>

4) Définissez CustomAction avec Id = "PrintEula" comme ceci:

<CustomAction Id="PrintEula" BinaryKey="WixUIWixca" DllEntry="PrintEula" Return="ignore" Execute="immediate" />

Remarque: BinaryKey est différent dans Wix3.0 par rapport à Wix2.0 et doit être exactement "WixUIWixca" (sensible à la casse).

Lorsque l'utilisateur appuie sur le bouton, il / elle sera présenté avec la boîte de dialogue standard Sélectionner l'imprimante et pourra imprimer à partir de là.


5
  • Nous affichons la version du produit quelque part (minuscule) dans le premier écran de l'interface graphique. Parce que les gens ont tendance à faire des erreurs en choisissant la bonne version à chaque fois. (Et gardez-nous les développeurs à la recherche des âges ..)

  • Nous avons configuré TFSBuild pour générer également des transformations (fichiers .mst) avec la configuration de nos différents environnements. (Nous connaissons tous les environnements dans lesquels nous devons déployer).

Comme le billet de blog original de Grant Holliday est en panne, je copie son contenu collé ici:


Tâche MSBuild pour générer des fichiers de transformation MSI à partir de XMLMarch 11 2008

Dans mon article précédent, j'ai décrit comment vous pouvez utiliser les fichiers de transformation MSI (* .mst) pour séparer les paramètres de configuration spécifiques à l'environnement d'un package MSI générique.

Bien que cela offre un niveau de flexibilité dans votre configuration, les fichiers Transform présentent deux inconvénients:

  1. C'est un format binaire
  2. Vous ne pouvez pas «éditer» ou «afficher» un fichier de transformation. Vous devez l'appliquer ou le recréer pour voir les modifications qu'il inclut.

Heureusement, nous pouvons utiliser la bibliothèque d'objets Microsoft Windows Installer (c: windowssystem32msi.dll) pour ouvrir des «bases de données» MSI et créer des fichiers de transformation.

Remerciements à Alex Shevchuk - De MSI à WiX - Partie 7 - Personnalisation de l'installation à l'aide de Transforms pour nous montrer comment y parvenir avec VbScript. Essentiellement, tout ce que j'ai fait est de prendre l'exemple d'Alex et en utilisant Interop.WindowsInstaller.dll, j'ai implémenté une tâche MSBuild. La tâche MSBuild

Téléchargez le code source et l'exemple transforms.xml ici (~ 7Kb Zipped VS2008 Solution)



2
Nous redéfinissons WelcomeDlgTitle dans mon fichier de localisation - fonctionne très bien! <String Id = "WelcomeDlgTitle"> {\ WixUI_Font_Bigger} Bienvenue dans l'assistant de configuration de [ProductName] [ProductVersion] </String>
saschabeaumont

5

Avant de déployer un package d'installation, je contrôle toujours son contenu.

C'est juste un simple appel sur la ligne de commande (selon le post de Terrences) ouvrez la ligne de commande et entrez

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

Cela extraira le contenu du package dans un sous-répertoire «Extraire» avec le chemin d'accès actuel.


4

Au lieu d'ORCA, utilisez InstEd qui est un bon outil pour afficher les tables MSI. Il a également la possibilité de différencier deux packages par Transform -> Compare To ...

De plus, une version Plus avec des fonctionnalités supplémentaires est disponible. Mais la version gratuite offre également une bonne alternative pour Orca.


4

Enregistrement d'assemblys .NET pour COM Interop avec compatibilité x86 / x64

NB Ce fragment est essentiellement le même que REGASM Assembly.dll / codebase

Deux choses se passent dans cet exemple, alors voici le code et je l'expliquerai par la suite ...

  <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 vous vous posiez la question, il s'agit en fait d'un pilote de télescope ASCOM .

Tout d'abord, j'ai suivi les conseils ci-dessus et créé quelques variables platforma dans un fichier séparé, vous pouvez les voir dispersées à travers le XML.

La partie si-alors-autre près du sommet traite de la compatibilité x86 vs x64. Mon assemblage cible "N'importe quel CPU", donc sur un système x64, je dois l'enregistrer deux fois, une fois dans le registre 64 bits et une fois dans les Wow6432Nodezones 32 bits . Le if-then-else me configure pour cela, les valeurs sont utilisées dans une foreachboucle plus tard. De cette façon, je n'ai à créer les clés de registre qu'une seule fois (principe DRY).

L'élément file spécifie la DLL d'assembly en cours d'installation et d'enregistrement:

<File Id="filDriverAssembly" Source="$(var.TiGra.Astronomy.AWRDriveSystem.TargetPath)" KeyPath="yes" Vital="yes" Assembly=".net" AssemblyApplication="filDriverAssembly"  />

Rien de révolutionnaire, mais remarquez que Assembly=".net"- cet attribut à lui seul ferait que l'assemblage soit placé dans le GAC, ce qui n'est PAS ce que je voulais. Utiliser l' AssemblyApplicationattribut pour pointer vers lui-même est simplement un moyen d'empêcher Wix de placer le fichier dans le GAC. Maintenant que Wix sait que c'est un assembly .net, il me permet d'utiliser certaines variables de liant dans mon XML, telles que !(bind.assemblyFullname.filDriverAssembly)pour obtenir le nom complet de l'assembly.


3

Définissez la DISABLEADVTSHORTCUTSpropriété pour forcer tous les raccourcis publiés dans votre programme d'installation à devenir des raccourcis normaux, et vous n'avez pas besoin d'inclure une clé de registre fictive à utiliser comme chemin de clé.

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

Je pense que Windows Installer 4.0 ou supérieur est une exigence .


2

C'est une belle structure mais d'après mon expérience, je me demande comment vous répondez à ces conditions:

A. Vos installations semblent toutes atterrir dans la même destination. Si un utilisateur doit installer les 3 versions à la fois, votre processus le permettra. Peuvent-ils dire sans ambiguïté quelle version de chaque exécutable ils déclenchent?

B. Comment gérez-vous les nouveaux fichiers qui existent dans TEST et / ou TRAINING mais pas encore dans LIVE?


Salut Blaine, A. Non, ils ne le font pas. InstallName est dans Config.wxi qui est le seul fichier non référencé par svn: externals. C'est donc unique pour chaque installation, c'est-à-dire pour chaque produit. C'est aussi pourquoi nous modifions les Guids pour chaque version. B. GOTO A. :) Ce sont des MSI distincts avec leur propre UpgradeCode.
si618

1
en passant, je comprends pourquoi vous avez répondu à ma question par une question, mais une fois que vous avez obtenu suffisamment de points de représentation, veuillez déplacer votre question dans les commentaires de réponse, sinon le fil sera difficile à suivre.
si618

2

Voici un moyen d'aider les grands projets Web à vérifier que le nombre de fichiers déployés correspond au nombre de fichiers intégrés dans un MSI (ou module de fusion). Je viens d'exécuter la tâche MSBuild personnalisée sur notre application principale (toujours en développement) et elle a détecté pas mal de fichiers manquants, principalement des images, mais quelques fichiers javascript s'étaient glissés vers!

Cette approche (jetant un œil à la table File de MSI en se connectant à la cible AfterBuild du projet WiX) pourrait fonctionner pour d'autres types d'application où vous avez accès à une liste complète des fichiers attendus.


2

Effectuer une réinstallation forcée lorsqu'une installation ne permet pas la désinstallation ou la réinstallation et ne revient pas en arrière.

Script VBscript utilisé pour remplacer une installation qui ne se désinstalle pas pour une raison quelconque.

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)

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.