Wie füge ich eine benutzerdefinierte WiX-Aktion hinzu, die nur bei der Deinstallation (über MSI) ausgeführt wird?

160

Ich möchte ein MSI-Installationsprogramm (erstellt über WiX ) so ändern, dass bei der Deinstallation ein gesamtes Verzeichnis gelöscht wird.

Ich verstehe die RemoveFileund RemoveFolderOptionen in WiX, aber diese sind nicht robust genug, um einen gesamten Ordner rekursiv zu löschen, dessen Inhalt nach der Installation erstellt wurde.

Ich habe die ähnliche Frage zum Stapelüberlauf beim Entfernen von Dateien bei der Deinstallation von WiX bemerkt , aber ich habe mich gefragt, ob dies einfacher durch einen Aufruf eines Batch-Skripts zum Löschen des Ordners möglich ist.

Ich benutze WiX zum ersten Mal und habe immer noch den Dreh raus mit benutzerdefinierten Aktionen . Was wäre ein grundlegendes Beispiel für eine benutzerdefinierte Aktion, bei der bei der Deinstallation ein Batch-Skript ausgeführt wird?

Gemeinschaft
quelle

Antworten:

188

EDIT : Vielleicht schauen Sie sich die Antwort direkt unten an .


Dieses Thema bereitet seit langem Kopfschmerzen. Ich habe es endlich herausgefunden. Es gibt einige Online-Lösungen, aber keine davon funktioniert wirklich. Und natürlich gibt es keine Dokumentation. In der folgenden Tabelle werden verschiedene Eigenschaften und deren Werte für verschiedene Installationsszenarien vorgeschlagen:

Alt-Text

In meinem Fall wollte ich eine Zertifizierungsstelle, die nur bei Deinstallationen ausgeführt werden kann - keine Upgrades, keine Reparaturen oder Änderungen. Nach der obigen Tabelle musste ich verwenden

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

Und es hat funktioniert!

nelsonjchen
quelle
25
Sind die Werte in diesem Diagramm korrekt? Warum sollten Sie REMOVE = "ALL" hinzufügen müssen? NOT UPGRADINGPRODUCTCODE gilt nur für eine Deinstallation (gemäß Tabelle), daher gilt (NOT UPGRADINGPRODUCTCODE) AND (REMOVE = "ALL") auch nur für eine Deinstallation. Das REMOVE = "ALL" scheint unnötig.
Todd Ropog
2
Ich stimme @ToddRopog zu - Das Beispiel und die Wahrheitstabelle scheinen nicht übereinzustimmen. Ist das wirklich richtig?
Tim Long
19
Die Wahrheitstabelle ist etwas falsch. NOT UPGRADINGPRODUCTCODE gilt auch für eine Erstinstallation
Neil
2
Allgemeine Bedingungen: alekdavis.blogspot.ru/2013/05/…
KindDragon
1
Bitte bestätigen Sie: Installiert und INSTALLIERT sind verschiedene Dinge, nur Installiert wird vom Windows Installer festgelegt. Ich denke nicht, dass INSTALLED funktioniert.
Micha Wiedenmann
139

Es gibt mehrere Probleme mit der Antwort von yaluna , auch bei Eigenschaftsnamen wird zwischen Groß- und Kleinschreibung unterschieden, es Installedwird die richtige Schreibweise angegeben ( INSTALLEDfunktioniert nicht). Die obige Tabelle hätte folgende sein sollen:

Geben Sie hier die Bildbeschreibung ein

Unter der Annahme einer vollständigen Reparatur und Deinstallation können die tatsächlichen Werte der Eigenschaften auch folgende sein:

Geben Sie hier die Bildbeschreibung ein

In der Dokumentation zur WiX Expression Syntax heißt es:

In diesen Ausdrücken können Sie Eigenschaftsnamen verwenden (denken Sie daran, dass zwischen Groß- und Kleinschreibung unterschieden wird).

Die Eigenschaften sind im Windows Installer-Handbuch dokumentiert (z. B. Installiert) ).

EDIT: Kleine Korrektur der ersten Tabelle; Offensichtlich kann "Deinstallieren" auch nur mit REMOVESein geschehen True.

ahmd0
quelle
3
REMOVE scheint auch für Change
szx
2
Ist das in der Spalte "Upgrade" während der Deinstallationssequenz der alten Version oder der Installationssequenz der neuen Version?
Nick Whaley
1
@NickWhaley: Ich habe mich eine Weile nicht damit befasst, aber ich glaube, dass die Option "Upgrade" nur verfügbar ist, wenn eine Version installiert wird, die größer ist als die bereits installierte.
ahmd0
1
@ ahmd0, natürlich. Es gibt jedoch eine verschachtelte Installation in RemoveExistingProducts mit völlig anderen Eigenschaften. Das steht in Ihrer Spalte "Upgrade". Der Rest des Upgrades ist identisch mit der Spalte "Installieren".
Nick Whaley
1
@NickWhaley: Die Option REMOVE gilt für größere Upgrades, dh 1.0.0 bis 2.0.0, nicht 1.0.0 bis 1.1.0, während der Ausführung des Deinstallationsprogramms der vorherigen Version. Um eine benutzerdefinierte Aktion während eines Haupt-Upgrades in der Installation der neuen Versionen auszuführen, müssen Sie auf die ActionProperty verweisen, die in Ihrer Upgrade-MSI-Tabelle für dieses Versions-Upgrade definiert ist. symantec.com/connect/articles/msi-upgrade-overview msdn.microsoft.com/en-us/library/aa372379%28v=vs.85%29.aspx
Chaoix
48

Sie können dies mit einer benutzerdefinierten Aktion tun. Sie können Ihrer benutzerdefinierten Aktion unter <InstallExecuteSequence>: eine Aktualisierung hinzufügen.

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

Dann müssen Sie auch Ihre Aktion definieren unter <Product>:

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

Wobei FileCleanerEXE eine Binärdatei ist (in meinem Fall ein kleines C ++ - Programm, das die benutzerdefinierte Aktion ausführt), die auch definiert ist unter <Product>:

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

Der eigentliche Trick dabei ist die Installed AND NOT UPGRADINGPRODUCTCODEBedingung für die benutzerdefinierte Aktion, ohne dass Ihre Aktion bei jedem Upgrade ausgeführt wird (da ein Upgrade wirklich eine Deinstallation und dann eine Neuinstallation ist). Was Sie beim Aktualisieren wahrscheinlich nicht möchten, wenn Sie Dateien löschen.

Nebenbei bemerkt: Ich empfehle, sich die Mühe zu machen, etwas wie ein C ++ - Programm zu verwenden, um die Aktion auszuführen, anstelle eines Batch-Skripts, da es über die Leistung und Kontrolle verfügt - und Sie können verhindern, dass das Fenster "cmd prompt" währenddessen blinkt Ihr Installationsprogramm wird ausgeführt.

csexton
quelle
3
25 positive Stimmen, aber keine akzeptierte Antwort. Willkommen in der Welt der Installateure! :)
Christopher Painter
4
Das funktioniert nicht wirklich. Wenn Sie eine fileCleaner.exe ausführen möchten, die in Ihrem eigenen Installationsordner installiert ist, handelt es sich um ein Henne-Ei-Problem: Das CustomActionwird "After = 'InstallFinalize'" ausgeführt. Zu diesem Zeitpunkt werden alle Dateien aus dem Installationsordner entfernt. Auch die fileCleaner.exe. Sie können es also nicht über eine CustomAction ausführen. Diese Antwort ist einfach falsch. Ich wundere mich über die 42 Upvotes!
Simon
40

Das größte Problem bei einem Batch-Skript ist die Behandlung des Rollbacks, wenn der Benutzer auf Abbrechen klickt (oder während der Installation ein Fehler auftritt). Die richtige Methode zur Behandlung dieses Szenarios besteht darin, eine CustomAction zu erstellen, die der RemoveFiles-Tabelle temporäre Zeilen hinzufügt. Auf diese Weise behandelt der Windows Installer die Rollback-Fälle für Sie. Es ist wahnsinnig einfacher, wenn Sie die Lösung sehen.

Damit eine Aktion nur während der Deinstallation ausgeführt wird, fügen Sie ein Bedingungselement hinzu mit:

REMOVE ~= "ALL"

Das ~ = sagt, dass Groß- und Kleinschreibung nicht berücksichtigt wird (obwohl ich denke, dass ALL immer Uppercaesd ist). Weitere Informationen finden Sie in der MSI SDK-Dokumentation zur BedingungssyntaxWeitere Informationen finden .

PS: Es gab noch nie einen Fall, in dem ich mich hinsetzte und dachte: "Oh, Batch-Datei wäre eine gute Lösung in einem Installationspaket." Wenn ich ein Installationspaket mit einer Batch-Datei finde, kann ich das Produkt nur gegen eine Rückerstattung zurücksenden.

Rob Mensching
quelle
Ich wollte gerade ein Batch-Skript verwenden und sah dann den PS-Abschnitt. Danke, dass du mich gerettet hast :) Das Entfernen ~ = "ALL" hat bei mir funktioniert.
ArNumb
12

Hier ist eine Reihe von Eigenschaften, die ich gemacht habe und die sich intuitiver anfühlen als die eingebauten. Die Bedingungen basieren auf der oben von ahmd0 angegebenen Wahrheitstabelle.

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

Hier einige Beispiele für die Verwendung:

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

Probleme:

Bill Tarbell
quelle
Dies ist eine großartige Lösung. Denken Sie daran, auch die Bedingungen PATCH und MSIPATCHREMOVE zu berücksichtigen.
Garet Jax
Wollten Sie in Ihrer Wahrheitstabelle PREVIOUSVERSIONSINSTALLED anstelle von UPGRADINGPRODUCTCODE verwenden, wie es von ahmd0 verwendet wird? Auf der Referenzseite für MSI-Eigenschaften ( docs.microsoft.com/en-us/windows/win32/msi/property-reference ) wird kein Verweis auf PREVIOUSVERSIONSINSTALLED angezeigt .
Patrick
Einige der Prädikate für Ihre Eigenschaften berücksichtigen nicht alle Zeilen in der Tabelle von ahmd0 (Installed, REINSTALL, UPGRADINGPRODUCTCODE und REMOVE). Würden Sie bitte erklären, warum?
Patrick
0

Ich habe Custom Action verwendet, die separat in der C ++ - DLL codiert ist, und die DLL verwendet, um die entsprechende Funktion zum Deinstallieren mit dieser Syntax aufzurufen:

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

Mit dem obigen Codeblock konnte ich bei der Deinstallation jede in der C ++ - DLL definierte Funktion ausführen. Zu Ihrer Information, meine Deinstallationsfunktion hatte Code zum Löschen aktueller Benutzerdaten und Registrierungseinträge.

Sid
quelle