web-dev-qa-db-fra.com

Trucs et astuces WiX

Nous utilisons WiX depuis un moment maintenant, et malgré les problèmes habituels de facilité d’utilisation, le système fonctionne assez bien. Ce que je recherche, ce sont des conseils utiles concernant:

  • Mise en place d'un projet WiX (mise en page, références, modèles de fichiers)
  • Intégration de WiX dans les solutions et processus de construction/libération
  • Configuration des installateurs pour les nouvelles installations et mises à niveau
  • Tous les bons hacks WiX que vous voudriez partager
264
si618
  1. Conservez les variables dans un fichier wxi include séparé. Permet la réutilisation, les variables sont plus rapides à trouver et (si nécessaire) permettent une manipulation plus facile par un outil externe.

  2. Définir des 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 la base de registre, ce qui permet aux mises à niveau de trouver le bon emplacement. Par exemple, si un utilisateur définit le 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 WiX Rob Mensching a posté un excellente entrée de blog qui donne plus de détails et corrige un cas Edge 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 est toujours de faire mises à niveau majeures , car elle autorise à la fois de nouvelles installations et de nouvelles mises à niveau dans un seul MSI. pgradeCode est fixé à un Guid unique et ne changera jamais, à moins que nous ne voulions pas mettre à jour le produit existant.

    Remarque : Dans WiX 3.5, il existe 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. À la sortie des versions, nous installons nos installateurs en copiant le fichier msi dans un répertoire de déploiement. Voici un exemple d'utilisation d'une cible wixproj appelée à partir de 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 des fichiers avec un 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écupère automatiquement la sortie 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 
    

    Un peu plus tard, robocopy efface les métadonnées de la copie de travail Subversion avant la récolte; la référence de répertoire racine -dr est définie sur notre emplacement d'installation plutôt que sur TARGETDIR par défaut; -var est utilisé pour créer une variable afin de 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 plan 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. Epargnez-vous un peu et suivez le conseil de Wim Coehen d'un composant par fichier. Cela vous permet également de laisser de côté (ou joker *) le GUID du composant .

  10. Rob Mensching a le choix astucieux de dépister rapidement les problèmes rencontrés 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 sur 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 la condition doit être l'inverse de ce que vous attendez, ce qui peut prêter à confusion :)

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

Vérifier 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 si IIS 6 La compatibilité de la métabase est installée 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>
38
Simon Steele

conservez tous les identifiants dans des espaces de noms séparés

  • Les fonctionnalités commencent par 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 CustomActions 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 identifiants dans toutes les différentes catégories.

34
Cheeso

Question fantastique. J'aimerais voir certaines bonnes pratiques illustrées.

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

J'ai un fichier source de niveau supérieur que j'appelle Product.wxs qui contient essentiellement la structure pour 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>

Le reste des fichiers .wix est composé de fragments contenant des groupes de composants référencés dans la balise Feature du fichier Product.wxs. Mon projet contient un regroupement logique de Nice 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 vacille un peu parce que les fragments doivent référencer des noms dans le fichier Product.wxs (par exemple, DirectoryRef), mais je trouve plus facile de gérer un seul fichier source volumineux. .

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

25
Peter Tate

Cochez la case 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'aspect "standard" n'est pas tout à fait correct. La case à cocher est toujours un fond gris, alors que la boîte de dialogue est blanche:

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

Une solution consiste à spécifiez votre propre ExitDialog personnalisé, avec une case à cocher située différemment . Cela fonctionne, mais cela semble être beaucoup de travail juste pour changer la couleur d'un contrôle. Un autre moyen de résoudre le même problème consiste à post-traiter le fichier MSI généré pour modifier les champs X, Y de la table Control 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 fichier MSI (à partir de light.exe) générera un fichier ExitDialog plus professionnel:

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

20
Cheeso

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

En un mot: Créez un code de mise à jour unique pour chaque installateur et définissez automatiquement le premier caractère de chaque guide pour chaque installateur, en laissant les 31 restants uniques.

Conditions préalables

Hypothèses

  • Les variables WiX permettent de définir UpgradeCode, ProductName, InstallName.
  • Vous avez déjà un installateur qui fonctionne. Je ne tenterais pas cela avant que vous ne le fassiez.
  • Tous vos composants sont conservés dans un seul fichier (Components.wxs). Ce processus fonctionnera si vous avez plusieurs fichiers, il vous restera plus de travail à faire.

Structure du répertoire

  • Setup.Library
    • Tous les fichiers wxs (composants, fonctionnalités, dialogues d'interface utilisateur, ...)
    • Common.Config.wxi (ProductCode = "*", ProductVersion, PlatformProgramFilesFolder, ...)
  • Setup.Live (wixproj)
    • Liez tous les fichiers Setup.Library en utilisant "Ajouter un fichier existant" -> "Ajouter en tant que lien" (le petit bouton fléché bas situé juste à côté du bouton Ajouter dans Visual Studio)
    • Config.wxi (a unique UpgradeCode, ProductName, InstallName, ...)
  • 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 (à l’exception de Config.wxi) du projet existant.
  • Créez Setup.Live, Setup.Test, etc. conformément à wixproj normal.
  • Ajouter une cible BeforeBuild dans wixproj dans Setup.Live, etc. pour exécuter la tâche de communauté MSBuild FileUpdate afin de modifier les Guids (j'ai utilisé A pour Live, B pour Test et C pour la formation)
  • Ajoutez la cible AfterBuild pour rétablir les indicateurs Components.wxs à 0.
  • Vérifiez avec Orca que chaque composant de chaque MSI dispose du 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érerais maintenant de laisser l'attribut Guid en composant (équivalent de *), en utilisant un fichier par composant et en le définissant comme chemin de clé. Cela évite d'avoir à appeler les cibles ModifyComponentsGuids et RevertComponentsGuids indiquées ci-dessous. Cela pourrait ne pas être possible pour tous vos composants cependant.

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 risque que quelqu'un mette à niveau Live au lieu de Training s'il est sur la même boîte et que vous n'utilisez que 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 majeure 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é de chaque installation.
  • Nous créons également des variables dans chaque fichier Config.wxi pour activer des noms de répertoires virtuels, des pools d’applications, des noms de bases de données uniques, etc. pour chaque programme d’installation.

PDATE 1:Guids des composants générés automatiquement élimine le besoin 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 clé.

PDATE 2: L'un des problèmes auxquels nous sommes confrontés est que si vous ne générez pas automatiquement votre composant Guid et que la construction échoue, les fichiers temporaires doivent être supprimés manuellement.

PDATE 3: Nous avons trouvé un moyen de supprimer la dépendance sur svn: externals et la création de fichiers temporaires. Cela rend le processus de construction plus résistant (et constitue la meilleure option si vous ne pouvez pas utiliser de joker pour les Guids) et moins fragile en cas d'échec de la compilation, que ce soit à la lumière ou à la bougie.

PDATE 4: La prise en charge de instances multiples , à l'aide de la transformation d'instance, est fournie dans WiX 3.0+ et mérite également un coup d'oeil.

18
si618

Utilisation de la journalisation Msi Diagnostic pour obtenir des informations détaillées sur les défaillances

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

Package.msi
c:\Package.log

Codes d'erreur Msi

Vidéo d'introduction Wix
La vidéo d'introduction de Oh et Random Wix présentant "Mr. WiX", Rob Mensching, est "une grande image conceptuelle" utile.

17
Terrance

Utilisez Javascript CustomActions car ils sont tellement faciles

Les gens ont dit que Javascript est la mauvaise chose à utiliser pour MSI CustomActions . Raisons donné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é l'écriture de CustomActions en Javascript très facile, beaucoup plus facile que d'utiliser C++. Plus vite. Et tout aussi fiable.

Un seul inconvénient est que Javascript CustomActions peut être extrait via Orca, alors qu’une autorité de certification C/C++ nécessiterait une ingénierie inverse. Si vous considérez que votre installateur est une propriété intellectuelle protégée, vous voudrez éviter les scripts.

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


Code "standard" Javascript 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
    };
}

Enregistrez ensuite 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 effectuer une requête WMI sur IIS afin d’obtenir une liste des sites Web existants sur lesquels un filtre ISAPI pouvait être installé. Cette liste a ensuite été utilisée pour renseigner une zone de liste affichée plus tard dans la séquence de l'interface utilisateur. Tout est très facile.

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

Question associée: À propos de Javascript CustomActions

17
Cheeso

Utiliser Heat.exe pour écraser le visage et infliger "Epic Pwnage" à de grandes installations

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

Translation: (Utilisation de la chaleur pour éviter de taper des fichiers individuels dans le projet à la main et pour automatiser les générations pour un processus plus simple.)

Syntaxe WiX 2.0 Heat détaillée

Pour les versions les plus récentes (pas si différentes des versions précédentes, mais des modifications de syntaxe potentiellement gênantes ...), accédez au répertoire Heat situé dans le cmd.exe et saisissez simplement heat, mais j'en ai un exemple ici pour obtenir de l'aide. avec des versions plus récentes si nécessaire.

Ajouter ce qui suit à votre événement de construction dans visual studio 2010.
(Cliquez avec le bouton droit de la souris sur Projet-> Propriétés -> Événements de construction-> Événements de pré-construction)

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

-bien joué 

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

-scom 

Ne prenez pas "fichiers COM"

-sreg 

Ne pas saisir les "fichiers du registre"

-sfrag 

Ne prenez pas "Fragments"

-srd 

Ne prenez pas la "racine Dir"

dir

dir indique que vous voulez que Heat regarde dans un dossier

"$ (EnviromentVariable)"

Le nom de la variable que vous ajouteriez aux variables de préprocesseur dans la section Propriétés du projet (> Projet de clic droit, Aller à propriétés) - Construire où il est indiqué Définir les variables de préprocesseur (suppose Visual Studio 2010).

Exemple: 
 EnviromentVariable = C:\Project\bin\Debug;
-cg GroupVariable 

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

FragmentDir

Le répertoire fragment dans lequel le fragment de sortie wxs sera stocké

NomFichier.wxs

Le nom du fichier

Tutoriel complet ici, donc freakin utile

Partie 1Partie 2

12
Terrance

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

aliasing d'annuaire

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

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

Ensuite, le produit principal peut aliaser l’un de ses répertoires (par exemple, "productInstallFolder") comme suit:

<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 faites maintenant référence au groupe de composants "B" dans votre configuration, car il s'agit d'une dépendance directe de votre application, le groupe de composants "A" sera automatiquement ajouté, même si l'auteur de l'application n'a jamais réalisé qu'il s'agissait d'une dépendance de "B". Cela "fonctionne" tant que vous n'avez aucune dépendance circulaire.

wixlib réutilisable

L'idée du graphe de dépendance ci-dessus fonctionne mieux si vous compilez les composants big-pool-o-réutilisable dans un wixlib réutilisable avec lit.exe. Lors de la création d'une configuration d'application, vous pouvez référencer cette 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
Wim Coenen

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

Essentiellement, vous créez une tâche MSBuild personnalisée pour exécuter un modèle T4. Ce modèle génère le WXS juste avant la compilation du projet Wix. Cela vous permet (en fonction de la manière 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 besoin de modifier les wxs chaque fois que vous ajoutez un nouvel assemblage).

12
Peter T. LaComb Jr.

Y compris les objets COM:

heat génère la plupart (sinon la totalité) des entrées de registre et des autres configurations nécessaires. Réjouir!

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

Utiliser heat sur 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 globalement disponible: la plupart du temps, vous n'en avez pas besoin de toute façon avec vos assemblys .NET - vous avez probablement déjà fait quelque chose de mal à ce stade si ce n'est pas prévu. une bibliothèque partagée), vous devez vous assurer de mettre à jour la clé de registre CodeBase afin qu'elle soit définie sur [#ComponentName]. Si vous prévoyez de l'installer dans le GAC (par exemple, vous avez créé une nouvelle bibliothèque commune géniale que tout le monde voudra utiliser), vous devez supprimer cette entrée et ajouter deux nouveaux attributs à l'élément File: Assembly et KeyPath. Assembly doit être défini sur ".net" et KeyPath sur "yes".

Cependant, certains environnements (en particulier tout ce qui a de la mémoire gérée, tels que les langages de script) devront également avoir accès à Typelib. Assurez-vous d'exécuter heat sur votre bibliothèque de types et incluez-le. heat générera toutes les clés de registre nécessaires. À quel point cela est cool?

9
Robert P

Installation sur C:\ProductName

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

Le code suivant peut être utilisé pour définir la propriété TARGETDIR à la racine du lecteur C: (extraite de la liste 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 sur C:\! Il pointe plutôt sur ROOTDRIVE qui pointe à son tour sur la racine du lecteur avec le plus grand espace libre ( voir ici ) - et cela n'est pas nécessairement le lecteur C:. Il pourrait y avoir un autre disque dur, une partition ou un lecteur USB!

Ensuite, quelque part sous votre balise <Product ...>, 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>
8
gehho

Variables d'environnement

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 (avec un script ou manuellement, peu importe). Dans votre source Wix, vous pouvez faire quelque chose comme:

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

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

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

Utilisation du modèle spécial "Mémoriser la propriété" de RobM

http://robmensching.com/blog/posts/2010/5/2/The-WiX-toolsets-Remember-Property-pattern

7
BozoJoe

Création d'une action personnalisée pour WIX écrite en code managé (C #) sans Votive

http://www.codeproject.com/KB/install/wixcustomaction.aspx

7
user431821

dialogues d'édition

L’utilisation de SharpDevelop dans une version 4.0.1.7090 (ou supérieure) est un bon moyen d’éditer des boîtes de dialogue. Avec l’aide de cet outil, une boîte de dialogue autonome (fichiers WXS provenant de sources WiX telles que InstallDirDlg.wxs) peut être ouverte, prévisualisée et modifiée en mode Création.

7
user432758

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" />
6
EdmundYeung99

Avant de déployer un paquet d'installation, j'en contrôle toujours le contenu.

C'est juste un simple appel en ligne de commande (selon Terrences post), 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 paquet dans un sous-répertoire 'Extract' avec le chemin actuel.

5
tobaer
  • 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 nous garder les développeurs à la recherche pour les âges ..)

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

Depuis que le blog original de Grant Holliday est en baisse, 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 précédent article, je décrivais comment vous pouvez utiliser les fichiers MSI Transform (* .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, il existe deux inconvénients aux fichiers Transform:

  1. C'est un format binaire
  2. Vous ne pouvez pas "éditer" ou "visualiser" un fichier de transformation. Vous devez l'appliquer ou le recréer pour voir quels changements 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.

Les crédits vont encore à Alex Shevchuk - De MSI à WiX - Partie 7 - Personnalisation de l’installation en utilisant Transforms pour nous montrer comment y parvenir avec VbScript. En gros, tout ce que j'ai fait est pris dans l'exemple d'Alex et en utilisant Interop.WindowsInstaller.dll, j'ai implémenté une tâche MSBuild. La tâche MSBuild

Télécharger le code source & exemple transforms.xml ici (~ Solution 7Kb Zipped VS2008)


5
thijs

Mettez les composants qui peuvent être patchés individuellement dans leurs propres fragments

Il en va de la création d'installateurs de produit et des correctifs que si vous incluez un composant dans un fragment, vous devez inclure tous les composants de ce fragment. Dans le cas de la création d'un programme d'installation, si vous manquez des références de composant, vous obtiendrez une erreur de liaison de light.exe. Cependant, lorsque vous créez un correctif, si vous incluez une seule référence de composant dans un fragment, alors 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>

De même, lors de l'application de la rubrique "Utilisation de Purely WiX" du fichier d'aide de 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 disposer simplement de la version 1.1 du produit product.wixpdb en utilisant les composants dans des fragments distincts. Veillez donc à fragmenter correctement votre produit avant son expédition.

5
Dave Andersen

Impression du CLUF à partir de Wix 3.0 et ultérieur

1) Lorsque vous compilez votre code source wix, le fichier light.exe doit référencer le fichier 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, la compilation de votre projet échoue, cela est probablement dû à des conflits d’ID de boîtes de dialogue, c’est-à-dire que votre projet utilisait les mêmes ID de boîtes de dialogue que certaines boîtes de dialogue standard de WixUIExtension.dll. attribuez des identifiants différents à vos dialogues. C'est un problème assez commun.

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 lorsqu’il imprime.

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

et un bouton-poussoir 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 l'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, la boîte de dialogue standard Sélectionner une imprimante lui est présentée et il est capable d'imprimer à partir de là.

5
farfareast

Modifiez le fichier "Prêt à installer?" Une boîte de dialogue (aka VerifyReadyDlg) pour fournir un résumé des choix effectués.

Cela ressemble à ceci:
texte alt http://i46.tinypic.com/s4th7t.jpg

Faites ceci avec un Javascript CustomAction:


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 l'autorité de certification Javascript:

<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'autorité de certification à un bouton. Dans cet exemple, l'autorité de certification est déclenchée lorsque vous cliquez sur Suivant à partir de CustomizeDlg:

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

En rapport SO Question: Comment définir, au moment de l'exécution, le texte à afficher dans VerifyReadyDlg?

5
Cheeso

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

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

4
user432758

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

NB Ce fragment est essentiellement identique à REGASM Assembly.dll/codebase

Quelques exemples se passent dans cet exemple, alors voici le code et je l'expliquerai plus tard ...

  <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 le demandiez, il s’agit en fait d’un pilote de télescope ASCOM .

Tout d’abord, j’ai pris conseil auprès d’en haut et créé des variables platforma dans un fichier séparé, que vous pouvez voir dispersées dans le fichier XML.

La partie if-then-else près du sommet concerne la compatibilité x86 vs x64. Mon assemblage cible "Tout processeur", donc sur un système x64, je dois l'enregistrer deux fois, une fois dans le registre 64 bits et une fois dans les zones Wow6432Node 32 bits. Le if-then-else me prépare pour cela, les valeurs sont utilisées ultérieurement dans une boucle foreach. De cette façon, je n'ai qu'à créer les clés de registre une seule fois (principe DRY).

L'élément file spécifie la dll 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 notez le Assembly=".net" - cet attribut à lui seul ferait placer l'Assemblée dans le GAC, ce qui n'est PAS ce que je voulais. Utiliser l'attribut AssemblyApplication pour se renvoyer à 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 assemblage .net, il me permet d'utiliser certaines variables de liant dans mon XML, telles que le !(bind.assemblyFullname.filDriverAssembly) pour obtenir le nom complet de l'assembly.

4
Tim Long

Définissez la propriété DISABLEADVTSHORTCUTS pour forcer tous les raccourcis annoncés dans votre programme d'installation à devenir des raccourcis normaux. Vous n'avez pas besoin d'inclure une clé de registre factice à utiliser comme chemin d'accès clé.

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

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

3
Dave Andersen

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 fichier MSI (ou un module de fusion). Je viens d'exécuter la tâche personnalisée MSBuild sur notre application principale (toujours en développement) et elle a récupéré quelques fichiers manquants, principalement des images, mais quelques fichiers javascript ont été perdus!

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

2
si618

Effectuer une réinstallation forcée lorsqu'une installation n'autorise pas la désinstallation ou la réinstallation et ne rétablit pas.

Script VBscript utilisé pour remplacer une installation qui ne 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)
2
Terrance

C'est une belle structure, mais en me basant sur mon expérience, je me demande comment vous gérez 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 indiquer sans ambiguïté quelle version de chaque exécutable ils déclenchent?

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

2
Blaine Wheeler

Créez une interface utilisateur qui a une action personnalisée qui définira une variable et qui désactivera/activera le bouton suivant (ou similaire) en fonction de la variable définie dans l'action personnalisée.

Pas aussi simple qu'on pourrait le penser, pas trop difficile, mais pas documentée nulle part!

Interactions Wix avec conditions, propriétés et actions personnalisées

2
Daniel Powell

Corrige ProgressDlg pour qu'il s'affiche correctement.

La taille de la police de mon installateur a été augmentée de 8 à 10 pour que la police devienne une échelle plus humaine et utilisable sur les moniteurs haute résolution. Je fais cela avec cette magie XML:

<UI Id="MyCustomUI">
  <TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="10" />
  <TextStyle Id="WixUI_Font_Big"    FaceName="Tahoma" Size="12" />
  <TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="14" />
  <TextStyle Id="WixUI_Font_Title"  FaceName="Tahoma" Size="12" Bold="yes" />

  <Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
</UI>

Mais cela signifie que ProgressDlg ne s’affiche plus correctement. C’est celui qui affiche la progression de l’installation, à la toute fin. L'ActionText étant tronqué, les descendeurs de lettres comme g et j ne s'affichent pas. Corrigez cela en ajustant la taille et la position des différentes commandes du Progressdialog, dans un Javascript post-traitement. Exécutez ce script après avoir généré le MSI:

var msiOpenDatabaseModeTransact = 1;
var filespec = WScript.Arguments(0);
var installer = new ActiveXObject("WindowsInstaller.Installer");
var database = installer.OpenDatabase(filespec, msiOpenDatabaseModeTransact);

// The text on the exit dialog is too close to the title.  This 
// step moves the text down from Y=70 to Y=90, about one line. 
sql = "UPDATE `Control` SET `Control`.`Y` = '90' " +
    "WHERE `Control`.`Dialog_`='ExitDialog' AND `Control`.`Control`='Description'";
view = database.OpenView(sql);
view.Execute();
view.Close();

// The progressbar is too close to the status text on the Progress dialog. 
// This step moves the progressbar down from Y=115 to Y=118, about 1/3 line. 
sql = "UPDATE `Control` SET `Control`.`Y` = '118' " +
    "WHERE `Control`.`Dialog_`='ProgressDlg' AND `Control`.`Control`='ProgressBar'";
view = database.OpenView(sql);
view.Execute();
view.Close();

// The StatusLabel and ActionText controls are too short on the Progress dialog,
// which means the bottom of the text is cut off.  This step
// increases the height from 10 to 16.
sql = "UPDATE `Control` SET `Control`.`Height` = '16' " +
    "WHERE `Control`.`Dialog_`='ProgressDlg' AND `Control`.`Control`='StatusLabel'";
view = database.OpenView(sql);
view.Execute();
view.Close();
sql = "UPDATE `Control` SET `Control`.`Height` = '16' " +
    "WHERE `Control`.`Dialog_`='ProgressDlg' AND `Control`.`Control`='ActionText'";
view = database.OpenView(sql);
view.Execute();
view.Close();

database.Commit();
1
Cheeso