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:
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.
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 ?>
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)">
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 !
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/" />
À 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>
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).
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>
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 .
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.
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>
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 < 600) OR IIS_METABASE_COMPAT)
</Condition>
conservez tous les identifiants dans des espaces de noms séparés
F.
Exemples: F.Documentation, F.Binaries, F.SampleCode.C.
Ex: C.ChmFile, C.ReleaseNotes, C.LicenseFile, C.IniFile, C.Registry.CA.
Ex: CA.LaunchHelp, CA.UpdateReadyDlg, CA.SetPropertyXFi.
Di.
Je trouve que cela aide énormément à garder une trace de tous les identifiants dans toutes les différentes catégories.
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!
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:
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:
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.
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="([a-f]|[A-F]|\d)" ReplacementText="Guid="A" />
</Target>
<!-- Revert the first character of every Guid back to initial value -->
<Target Name="RevertComponentsGuids">
<FileUpdate Files="..\Setup.Library\Components.wxs" Regex="Guid="([a-f]|[A-F]|\d)" ReplacementText="Guid="0" />
</Target>
Dernières pensées
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.
Utilisation de la journalisation Msi Diagnostic pour obtenir des informations détaillées sur les défaillancesmsiexec /i Package.msi /l*v c:\Package.log
Où
Package.msi
c:\Package.log
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.
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
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
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.
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).
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?
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>
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)" />
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
Création d'une action personnalisée pour WIX écrite en code managé (C #) sans Votive
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.
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" />
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.
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:
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)
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.
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à.
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?
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.
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.
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 .
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.
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)
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?
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
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();