Comment ajouter une action personnalisée WiX qui se produit uniquement lors de la désinstallation (via MSI)?

161

Je voudrais modifier un programme d'installation MSI (créé via WiX ) pour supprimer un répertoire entier lors de la désinstallation.

Je comprends les options RemoveFileet RemoveFolderde WiX, mais celles-ci ne sont pas assez robustes pour supprimer de manière récursive un dossier entier dont le contenu a été créé après l'installation.

J'ai remarqué la question similaire de Stack Overflow Suppression de fichiers lors de la désinstallation de WiX , mais je me demandais si cela pouvait être fait plus simplement en utilisant un appel à un script batch pour supprimer le dossier.

C'est la première fois que j'utilise WiX et je suis toujours en train de maîtriser les actions personnalisées . Quel serait un exemple de base d'action personnalisée qui exécutera un script batch lors de la désinstallation?

Communauté
la source

Réponses:

188

EDIT : Regardez peut-être la réponse actuellement immédiatement ci-dessous .


Ce sujet est un casse-tête depuis longtemps. J'ai finalement compris. Il existe des solutions en ligne, mais aucune d'entre elles ne fonctionne vraiment. Et bien sûr, il n'y a pas de documentation. Ainsi, dans le tableau ci-dessous, il est suggéré d'utiliser plusieurs propriétés et les valeurs qu'elles ont pour divers scénarios d'installation:

texte alternatif

Donc, dans mon cas, je voulais une autorité de certification qui ne fonctionnera que sur les désinstallations - pas les mises à niveau, pas les réparations ou les modifications. D'après le tableau ci-dessus, j'ai dû utiliser

<Custom Action='CA_ID' Before='other_CA_ID'>
        (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")</Custom>

Et ça a marché!

Nelsonjchen
la source
25
Les valeurs de ce graphique sont-elles correctes? Pourquoi auriez-vous besoin d'ajouter REMOVE = "ALL"? NOT UPGRADINGPRODUCTCODE n'est vrai que pour une désinstallation (selon le graphique), donc (NOT UPGRADINGPRODUCTCODE) AND (REMOVE = "ALL") ne serait également vrai que lors d'une désinstallation. Le REMOVE = "ALL" semble inutile.
Todd Ropog
2
Je suis d'accord avec @ToddRopog - L'exemple et la table de vérité ne semblent pas d'accord. Est-ce vraiment correct?
Tim Long
19
La table de vérité est légèrement fausse. NOT UPGRADINGPRODUCTCODE est également vrai pour une première installation
Neil
2
Conditions communes: alekdavis.blogspot.ru/2013/05/…
KindDragon
1
Veuillez confirmer: Installé et INSTALLÉ sont des choses différentes, seul Installé est défini par Windows Installer. Je ne pense pas que INSTALLED fonctionne.
Micha Wiedenmann le
140

Il y a plusieurs problèmes avec la réponse de yaluna , aussi les noms de propriété sont sensibles à la casse, Installedest l'orthographe correcte ( INSTALLEDne fonctionnera pas). Le tableau ci-dessus aurait dû être le suivant:

entrez la description de l'image ici

En supposant également une réparation et une désinstallation complètes, les valeurs réelles des propriétés pourraient être:

entrez la description de l'image ici

La documentation de la syntaxe d'expression WiX dit:

Dans ces expressions, vous pouvez utiliser des noms de propriété (rappelez-vous qu'ils sont sensibles à la casse).

Les propriétés sont documentées dans le guide Windows Installer (par exemple installé )

EDIT: Petite correction au premier tableau; évidemment "Désinstaller" peut également se produire avec le simple fait d' REMOVEêtre True.

ahmd0
la source
3
REMOVE semble également être réglé pour le changement
szx
2
La colonne «Mise à jour», est-ce pendant la séquence de désinstallation de l'ancienne version ou la séquence d'installation de la nouvelle version?
Nick Whaley
1
@NickWhaley: Je ne l'ai pas regardé depuis un moment mais je crois que l'option "Upgrade" est uniquement lors de l'installation d'une version supérieure à celle déjà installée.
ahmd0
1
@ ahmd0, bien sûr. Mais il existe une installation imbriquée qui se produit dans RemoveExistingProducts qui a un ensemble de propriétés totalement différent. C'est ce qui se trouve dans votre colonne «Upgrade». Le reste de la mise à niveau est identique à la colonne «Installer».
Nick Whaley
1
@NickWhaley: L'option REMOVE sera vraie pour les mises à niveau majeures, c'est-à-dire 1.0.0 à 2.0.0, et non 1.0.0 à 1.1.0, pendant l'exécution du programme de désinstallation de la version précédente. Pour exécuter une action personnalisée lors d'une mise à niveau majeure dans l'installation des nouvelles versions, vous devez référencer l'ActionProperty définie dans votre table MSI de mise à niveau pour cette mise à niveau de version. symantec.com/connect/articles/msi-upgrade-overview msdn.microsoft.com/en-us/library/aa372379%28v=vs.85%29.aspx
Chaoix
48

Vous pouvez le faire avec une action personnalisée. Vous pouvez ajouter une référence à votre action personnalisée sous <InstallExecuteSequence>:

<InstallExecuteSequence>
...
  <Custom Action="FileCleaner" After='InstallFinalize'>
          Installed AND NOT UPGRADINGPRODUCTCODE</Custom>

Ensuite, vous devrez également définir votre Action sous <Product>:

<Product> 
...
  <CustomAction Id='FileCleaner' BinaryKey='FileCleanerEXE' 
                ExeCommand='' Return='asyncNoWait'  />

Où FileCleanerEXE est un binaire (dans mon cas, un petit programme c ++ qui fait l'action personnalisée) qui est également défini sous <Product>:

<Product> 
...
  <Binary Id="FileCleanerEXE" SourceFile="path\to\fileCleaner.exe" />

La vraie astuce pour cela est la Installed AND NOT UPGRADINGPRODUCTCODEcondition de l'action personnalisée, sans que votre action soit exécutée à chaque mise à niveau (puisqu'une mise à niveau est en réalité une désinstallation puis une réinstallation). Ce qui, si vous supprimez des fichiers, n'est probablement pas souhaité lors de la mise à niveau.

Sur une note latérale: je recommande de passer par la peine d'utiliser quelque chose comme le programme C ++ pour faire l'action, au lieu d'un script batch en raison de la puissance et du contrôle qu'il fournit - et vous pouvez empêcher la fenêtre "cmd prompt" de clignoter pendant votre programme d'installation s'exécute.

csexton
la source
3
25 votes positifs mais pas une réponse acceptée. Bienvenue dans le monde des installateurs! :)
Christopher Painter
4
Cela ne fonctionne pas vraiment. Lorsque vous voulez exécuter un fileCleaner.exe, qui est installé dans votre propre dossier d'installation, ce sera un problème de poule et d'oeuf: Le CustomActionsera exécuté "After = 'InstallFinalize'". À ce stade, tous les fichiers sont supprimés du dossier d'installation. Aussi le fileCleaner.exe. Vous ne pouvez donc pas l'exécuter via une CustomAction. Cette réponse est tout simplement fausse. Je m'interroge sur les 42 votes positifs!
Simon
40

Le plus gros problème avec un script batch est la gestion de la restauration lorsque l'utilisateur clique sur Annuler (ou quelque chose ne va pas pendant votre installation). La manière correcte de gérer ce scénario consiste à créer une CustomAction qui ajoute des lignes temporaires à la table RemoveFiles. De cette façon, Windows Installer gère les cas de restauration pour vous. C'est incroyablement plus simple quand vous voyez la solution.

Quoi qu'il en soit, pour qu'une action ne s'exécute que lors de la désinstallation, ajoutez un élément Condition avec:

REMOVE ~= "ALL"

le ~ = dit comparer insensible à la casse (même si je pense que ALL est toujours uppercaesd). Consultez la documentation du SDK MSI sur la syntaxe des conditions pour plus d'informations.

PS: Il n'y a jamais eu de cas où je me suis assis et j'ai pensé: "Oh, le fichier batch serait une bonne solution dans un package d'installation." En fait, trouver un package d'installation contenant un fichier de commandes ne ferait que m'encourager à retourner le produit pour un remboursement.

Rob Mensching
la source
J'étais sur le point d'utiliser un script batch, puis j'ai vu la section PS. Merci de m'avoir sauvé :) Le Remove ~ = "ALL" a fonctionné pour moi.
ArNumb
12

Voici un ensemble de propriétés que j'ai rendues plus intuitives à utiliser que les éléments intégrés. Les conditions sont basées sur la table de vérité fournie ci-dessus par ahmd0.

<!-- truth table for installer varables (install vs uninstall vs repair vs upgrade) https://stackoverflow.com/a/17608049/1721136 -->
 <SetProperty Id="_INSTALL"   After="FindRelatedProducts" Value="1"><![CDATA[Installed="" AND PREVIOUSVERSIONSINSTALLED=""]]></SetProperty>
 <SetProperty Id="_UNINSTALL" After="FindRelatedProducts" Value="1"><![CDATA[PREVIOUSVERSIONSINSTALLED="" AND REMOVE="ALL"]]></SetProperty>
 <SetProperty Id="_CHANGE"    After="FindRelatedProducts" Value="1"><![CDATA[Installed<>"" AND REINSTALL="" AND PREVIOUSVERSIONSINSTALLED<>"" AND REMOVE=""]]></SetProperty>
 <SetProperty Id="_REPAIR"    After="FindRelatedProducts" Value="1"><![CDATA[REINSTALL<>""]]></SetProperty>
 <SetProperty Id="_UPGRADE"   After="FindRelatedProducts" Value="1"><![CDATA[PREVIOUSVERSIONSINSTALLED<>"" ]]></SetProperty>

Voici quelques exemples d'utilisation:

  <Custom Action="CaptureExistingLocalSettingsValues" After="InstallInitialize">NOT _UNINSTALL</Custom>
  <Custom Action="GetConfigXmlToPersistFromCmdLineArgs" After="InstallInitialize">_INSTALL OR _UPGRADE</Custom>
  <Custom Action="ForgetProperties" Before="InstallFinalize">_UNINSTALL OR _UPGRADE</Custom>
  <Custom Action="SetInstallCustomConfigSettingsArgs" Before="InstallCustomConfigSettings">NOT _UNINSTALL</Custom>
  <Custom Action="InstallCustomConfigSettings" Before="InstallFinalize">NOT _UNINSTALL</Custom>

Problèmes:

Bill Tarbell
la source
C'est une excellente solution. N'oubliez pas de prendre également en compte les conditions PATCH et MSIPATCHREMOVE.
Garet Jax
Dans votre table de vérité, vouliez-vous utiliser PREVIOUSVERSIONSINSTALLED au lieu de UPGRADINGPRODUCTCODE tel qu'il est utilisé par ahmd0? Je ne vois aucune référence à PREVIOUSVERSIONSINSTALLED sur la page de référence de la propriété MSI ( docs.microsoft.com/en-us/windows/win32/msi/property-reference ).
Patrick
Plusieurs des prédicats de vos propriétés ne prennent pas en compte toutes les lignes de la table de ahmd0 (Installed, REINSTALL, UPGRADINGPRODUCTCODE et REMOVE). Pouvez-vous expliquer pourquoi?
Patrick
0

J'ai utilisé l'action personnalisée codée séparément dans la DLL C ++ et utilisé la DLL pour appeler la fonction appropriée lors de la désinstallation en utilisant cette syntaxe:

<CustomAction Id="Uninstall" BinaryKey="Dll_Name" 
              DllEntry="Function_Name" Execute="deferred" />

En utilisant le bloc de code ci-dessus, j'ai pu exécuter n'importe quelle fonction définie dans la DLL C ++ lors de la désinstallation. Pour info, ma fonction de désinstallation avait un code concernant la suppression des données utilisateur actuelles et des entrées de registre.

Sid
la source