Come aggiungere un'azione personalizzata WiX che si verifica solo durante la disinstallazione (tramite MSI)?


161

Vorrei modificare un programma di installazione MSI (creato tramite WiX ) per eliminare un'intera directory durante la disinstallazione.

Comprendo le opzioni RemoveFilee RemoveFolderin WiX, ma queste non sono abbastanza robuste da eliminare in modo ricorsivo un'intera cartella con contenuti creati dopo l'installazione.

Ho notato la domanda simile Stack Overflow Rimozione di file durante la disinstallazione di WiX , ma mi chiedevo se ciò potesse essere fatto più semplicemente usando una chiamata a uno script batch per eliminare la cartella.

Questa è la prima volta che utilizzo WiX e sto ancora imparando le azioni personalizzate . Quale sarebbe un esempio di base di un'azione personalizzata che eseguirà uno script batch alla disinstallazione?

Risposte:


188

EDIT : forse guarda la risposta attualmente immediatamente sotto .


Questo argomento è stato un mal di testa per molto tempo. Alla fine l'ho capito. Ci sono alcune soluzioni online, ma nessuna di queste funziona davvero. E ovviamente non c'è documentazione. Quindi nella tabella seguente ci sono diverse proprietà che si consiglia di utilizzare e i valori che hanno per vari scenari di installazione:

testo alternativo

Quindi nel mio caso volevo una CA che funzionasse solo su disinstallazioni, non su aggiornamenti, non su riparazioni o modifiche. Secondo la tabella sopra ho dovuto usare

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

E ha funzionato!


25
I valori in quel grafico sono corretti? Perché dovresti aggiungere REMOVE = "ALL"? NOT UPGRADINGPRODUCTCODE è vero solo per una disinstallazione (secondo il grafico), quindi (NOT UPGRADINGPRODUCTCODE) E (REMOVE = "ALL") sarebbe vero solo su una disinstallazione. REMOVE = "ALL" non sembra necessario.
Todd Ropog,

2
Sono d'accordo con @ToddRopog - L'esempio e la tabella della verità non sembrano concordare. È davvero corretto?
Tim Long,

19
La tabella della verità è leggermente sbagliata. NON AGGIORNARE IL CODICE PRODOTTO è vero anche per una prima installazione
Neil


1
Conferma: Installato e INSTALLATO sono cose diverse, solo Installato è impostato da Windows Installer. Non penso che INSTALLATO funzioni.
Micha Wiedenmann,

140

Ci sono molti problemi con la risposta di yaluna , anche i nomi delle proprietà fanno distinzione tra maiuscole e minuscole, Installedè l'ortografia corretta ( INSTALLEDnon funzionerà). La tabella sopra avrebbe dovuto essere questa:

inserisci qui la descrizione dell'immagine

Supponendo anche una riparazione completa e la disinstallazione, i valori effettivi delle proprietà potrebbero essere:

inserisci qui la descrizione dell'immagine

La documentazione sulla sintassi di WiX Expression dice:

In queste espressioni, puoi usare i nomi delle proprietà (ricorda che fanno distinzione tra maiuscole e minuscole).

Le proprietà sono documentate nella Guida all'installazione di Windows (ad es. Installata )

EDIT: piccola correzione alla prima tabella; evidentemente "Disinstalla" può anche accadere solo con l' REMOVEessere True.


3
Sembra anche che REMOVE sia impostato per Change
szx

2
La colonna "Aggiorna" è quella durante la sequenza di disinstallazione della vecchia versione o la sequenza di installazione della nuova versione?
Nick Whaley,

1
@NickWhaley: non ci ho pensato per un po ', ma credo che l'opzione "Aggiorna" sia solo quando si installa una versione successiva a quella già installata.
ahmd0,

1
@ ahmd0, ovviamente. Ma esiste un'installazione nidificata che si verifica in RemoveExistingProducts con un set di proprietà totalmente diverso. Questo è ciò che si trova nella colonna "Aggiornamento". Il resto dell'aggiornamento è identico alla colonna "Installa".
Nick Whaley,

1
@NickWhaley: l'opzione RIMUOVI sarà vera per gli aggiornamenti principali, ovvero da 1.0.0 a 2.0.0, non da 1.0.0 a 1.1.0, durante l'esecuzione del programma di disinstallazione della versione precedente. Per eseguire un'azione personalizzata durante un aggiornamento importante nelle installazioni delle nuove versioni, è necessario fare riferimento a ActionProperty definito nella tabella MSI di aggiornamento per tale aggiornamento di versione. symantec.com/connect/articles/msi-upgrade-overview msdn.microsoft.com/en-us/library/aa372379%28v=vs.85%29.aspx
Chaoix

48

Puoi farlo con un'azione personalizzata. Puoi aggiungere un riferimento all'azione personalizzata sotto <InstallExecuteSequence>:

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

Quindi dovrai anche definire la tua azione in <Product>:

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

Dove FileCleanerEXE è un file binario (nel mio caso un piccolo programma c ++ che esegue l'azione personalizzata) che è anche definito in <Product>:

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

Il vero trucco è la Installed AND NOT UPGRADINGPRODUCTCODEcondizione dell'azione personalizzata, senza che l'azione venga eseguita ad ogni aggiornamento (poiché un aggiornamento è in realtà una disinstallazione, quindi reinstallare). Che, se si stanno eliminando i file, probabilmente non lo si desidera durante l'aggiornamento.

Una nota a margine: ti consiglio di affrontare il problema di utilizzare qualcosa come il programma C ++ per eseguire l'azione, anziché uno script batch a causa della potenza e del controllo che fornisce - e puoi impedire alla finestra "cmd prompt" di lampeggiare mentre il tuo programma di installazione viene eseguito.


3
25 voti positivi ma non una risposta accettata. Benvenuti nel mondo degli installatori! :)
Christopher Painter,

4
Questo non funziona davvero. Quando si desidera eseguire un fileCleaner.exe, che è installato nella propria cartella di installazione, si verificherà un problema di tipo "egg-and-egg": CustomActionverrà eseguito "After = 'InstallFinalize'". A questo punto, tutti i file vengono rimossi dalla cartella di installazione. Anche il fileCleaner.exe. Quindi non puoi eseguirlo tramite un CustomAction. Questa risposta è semplicemente sbagliata. Mi chiedo i 42 voti!
Simon,

40

Il problema più grande con uno script batch è la gestione del rollback quando l'utente fa clic su Annulla (o qualcosa va storto durante l'installazione). Il modo corretto di gestire questo scenario è creare un CustomAction che aggiunge righe temporanee alla tabella RemoveFiles. In questo modo Windows Installer gestisce i casi di rollback per te. È follemente più semplice quando vedi la soluzione.

Ad ogni modo, per eseguire un'azione solo durante la disinstallazione aggiungere un elemento Condition con:

REMOVE ~= "ALL"

il ~ = dice che non è sensibile al maiuscolo / minuscolo (anche se penso che TUTTO sia sempre sopraffatto). Vedere la documentazione dell'SDK MSI sulla sintassi delle condizioni per ulteriori informazioni.

PS: Non c'è mai stato un caso in cui mi sono seduto e ho pensato: "Oh, il file batch sarebbe una buona soluzione in un pacchetto di installazione". In realtà, trovare un pacchetto di installazione che contiene un file batch mi incoraggerebbe solo a restituire il prodotto per un rimborso.


Stavo per usare uno script batch e poi ho visto la sezione PS. Grazie per avermi salvato:) Remove ~ = "ALL" ha funzionato per me.
ArNumb,

12

Ecco una serie di proprietà che ho creato che sembrano più intuitive da usare rispetto alle cose integrate. Le condizioni si basano sulla tabella della verità fornita sopra da 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>

Ecco alcuni esempi di utilizzo:

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

Problemi:


Questa è un'ottima soluzione Ricorda di considerare anche le condizioni PATCH e MSIPATCHREMOVE.
Garet Jax,

Nella tua tabella di verità, intendevi usare PREVIOUSVERSIONSINSTALLED invece di UPGRADINGPRODUCTCODE come viene usato da ahmd0? Non vedo alcun riferimento a PREVIOUSVERSIONSINSTALLED nella pagina di riferimento della proprietà MSI ( docs.microsoft.com/en-us/windows/win32/msi/property-reference ).
Patrick,

Molti dei predicati per le tue proprietà non tengono conto di tutte le righe nella tabella di ahmd0 (Installato, REINSTALL, UPGRADINGPRODUCTCODE e REMOVE). Potresti spiegare perché?
Patrick

0

Ho usato l'azione personalizzata codificata separatamente nella DLL C ++ e ho usato la DLL per chiamare la funzione appropriata durante la disinstallazione usando questa sintassi:

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

Usando il blocco di codice sopra, sono stato in grado di eseguire qualsiasi funzione definita nella DLL C ++ al momento della disinstallazione. Cordiali saluti, la mia funzione di disinstallazione aveva il codice relativo alla cancellazione dei dati dell'utente corrente e delle voci di registro.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.