Jak dodać niestandardową akcję WiX, która dzieje się tylko podczas odinstalowywania (przez MSI)?


161

Chciałbym zmodyfikować instalator MSI (utworzony za pomocą WiX ), aby usunąć cały katalog podczas odinstalowywania.

Rozumiem opcje RemoveFilei RemoveFolderw WiX, ale nie są one wystarczająco solidne, aby rekurencyjnie usunąć cały folder zawierający zawartość utworzoną po instalacji.

Zauważyłem podobne pytanie dotyczące przepełnienia stosu Usuwanie plików podczas odinstalowywania WiX , ale zastanawiałem się, czy można to zrobić prościej, używając wywołania skryptu wsadowego, aby usunąć folder.

Po raz pierwszy używam WiX i wciąż rozumiem niestandardowe akcje . Jaki byłby podstawowy przykład niestandardowej akcji, która uruchomi skrypt wsadowy podczas odinstalowywania?

Odpowiedzi:


188

EDYCJA : Być może spójrz na odpowiedź obecnie bezpośrednio poniżej .


Ten temat od dawna przyprawia o ból głowy. W końcu to rozgryzłem. Istnieje kilka rozwiązań online, ale żadne z nich tak naprawdę nie działa. I oczywiście nie ma dokumentacji. Dlatego na poniższym wykresie przedstawiono kilka właściwości, których użycie jest zalecane, oraz wartości, jakie mają one dla różnych scenariuszy instalacji:

tekst alternatywny

Więc w moim przypadku chciałem CA, który będzie działał tylko przy odinstalowywaniu - a nie uaktualnieniach, nie naprawach ani modyfikacjach. Zgodnie z powyższą tabelą musiałem użyć

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

I zadziałało!


25
Czy wartości na tym wykresie są prawidłowe? Dlaczego miałbyś dodać REMOVE = "ALL"? NIE UPGRADINGPRODUCTCODE jest prawdziwe tylko w przypadku dezinstalacji (zgodnie z wykresem), więc (NOT UPGRADINGPRODUCTCODE) AND (REMOVE = "ALL") będzie również prawdziwe tylko w przypadku dezinstalacji. REMOVE = „ALL” wydaje się niepotrzebne.
Todd Ropog

2
Zgadzam się z @ToddRopog - przykład i tabela prawdy wydają się nie zgadzać. Czy to naprawdę prawda?
Tim Long

19
Tabela prawdy jest nieco błędna. NIE AKTUALIZACJA KODU PRODUKTU obowiązuje również dla pierwszej instalacji
Neil


1
Potwierdź: Zainstalowane i ZAINSTALOWANE to różne rzeczy, tylko Zainstalowana jest ustawiana przez Instalatora Windows. Nie sądzę, że INSTALLED działa.
Micha Wiedenmann

140

Istnieje wiele problemów z odpowiedzią yaluna , również nazwy nieruchomości uwzględniają wielkość liter, Installedjest poprawna pisownia ( INSTALLEDnie będzie działać). Powyższa tabela powinna wyglądać następująco:

wprowadź opis obrazu tutaj

Zakładając również pełną naprawę i odinstalowanie, rzeczywiste wartości właściwości mogą być następujące:

wprowadź opis obrazu tutaj

Dokumentacja składni wyrażeń WiX mówi:

W tych wyrażeniach możesz używać nazw właściwości (pamiętaj, że rozróżniają one wielkość liter).

Właściwości są udokumentowane w Przewodniku Instalatora Windows (np. Zainstalowane )

EDYCJA: Mała poprawka do pierwszej tabeli; ewidentnie „Odinstaluj” może się również zdarzyć po prostu REMOVEbędąc True.



2
Kolumna „Uaktualnij” - czy to podczas sekwencji odinstalowywania starej wersji, czy sekwencji instalacji nowej wersji?
Nick Whaley

1
@NickWhaley: Nie zaglądałem do tego od jakiegoś czasu, ale uważam, że opcja „Uaktualnij” jest dostępna tylko wtedy, gdy instaluję wersję wyższą niż ta już zainstalowana.
ahmd0

1
@ ahmd0, oczywiście. Ale istnieje instalacja zagnieżdżona, która występuje w RemoveExistingProducts, która ma zupełnie inny zestaw właściwości. To jest to, co znajduje się w kolumnie „Uaktualnij”. Reszta aktualizacji jest identyczna jak w kolumnie „Zainstaluj”.
Nick Whaley

1
@NickWhaley: Opcja REMOVE będzie aktywna dla głównych aktualizacji, tj. 1.0.0 do 2.0.0, a nie 1.0.0 do 1.1.0, podczas wykonywania deinstalatora poprzedniej wersji. Aby uruchomić akcję niestandardową podczas dużej aktualizacji w instalacji nowych wersji, musisz odwołać się do właściwości ActionProperty zdefiniowanej w tabeli aktualizacji MSI dla aktualizacji tej wersji. symantec.com/connect/articles/msi-upgrade-overview msdn.microsoft.com/en-us/library/aa372379%28v=vs.85%29.aspx
Chaoix

48

Możesz to zrobić za pomocą akcji niestandardowej. Możesz dodać odwołanie do akcji niestandardowej w <InstallExecuteSequence>:

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

Następnie musisz również zdefiniować swoją akcję w <Product>:

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

Gdzie FileCleanerEXE to plik binarny (w moim przypadku mały program C ++, który wykonuje niestandardową akcję), który jest również zdefiniowany w <Product>:

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

Prawdziwą sztuczką jest Installed AND NOT UPGRADINGPRODUCTCODEsytuacja w Akcji niestandardowej, bez tego, że akcja będzie uruchamiana przy każdej aktualizacji (ponieważ aktualizacja to tak naprawdę odinstalowanie, a następnie ponowna instalacja). Co, jeśli usuwasz pliki, prawdopodobnie nie jest pożądane podczas aktualizacji.

Na marginesie: zalecam rozwiązanie problemu z użyciem czegoś takiego jak program C ++ do wykonania tej akcji, zamiast skryptu wsadowego ze względu na jego moc i kontrolę - i możesz zapobiec miganiu okna „zachęty cmd” podczas Twój instalator działa.


3
25 głosów za, ale nie zaakceptowana odpowiedź. Witamy w świecie instalatorów! :)
Christopher Painter,

4
To naprawdę nie działa. Jeśli chcesz uruchomić plik fileCleaner.exe, który jest zainstalowany we własnym folderze instalacyjnym, będzie to problem z jajkiem i kury: CustomActionzostanie wykonany „After = 'InstallFinalize'”. W tym momencie wszystkie pliki są usuwane z folderu instalacyjnego. Również plik fileCleaner.exe. Dlatego nie możesz go wykonać za pomocą CustomAction. Ta odpowiedź jest po prostu błędna. Zastanawiam się nad 42 głosami za!
Simon,

40

Największym problemem związanym ze skryptem wsadowym jest obsługa wycofywania, gdy użytkownik kliknie przycisk Anuluj (lub coś pójdzie nie tak podczas instalacji). Prawidłowym sposobem obsługi tego scenariusza jest utworzenie CustomAction, która dodaje tymczasowe wiersze do tabeli RemoveFiles. W ten sposób Instalator Windows obsługuje przypadki wycofywania zmian. Jest to szalenie prostsze, gdy widzisz rozwiązanie.

W każdym razie, aby akcja była wykonywana tylko podczas deinstalacji, dodaj element warunku z:

REMOVE ~= "ALL"

~ = mówi porównaj bez rozróżniania wielkości liter (chociaż myślę, że WSZYSTKO jest zawsze wielkie). Więcej informacji można znaleźć w dokumentacji zestawu MSI SDK na temat składni warunków .

PS: Nigdy nie było przypadku, gdybym usiadł i pomyślał: „Och, plik wsadowy byłby dobrym rozwiązaniem w pakiecie instalacyjnym”. Właściwie znalezienie pakietu instalacyjnego zawierającego plik wsadowy tylko zachęciłoby mnie do zwrotu produktu w celu uzyskania zwrotu pieniędzy.


Miałem właśnie użyć skryptu wsadowego, a potem zobaczyłem sekcję PS. Dzięki za uratowanie mnie :) Usuń ~ = "WSZYSTKO" działało dla mnie.
ArNumb

12

Oto zestaw właściwości, które stworzyłem, które są bardziej intuicyjne w użyciu niż wbudowane elementy. Warunki są oparte na tabeli prawdy dostarczonej powyżej przez 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>

Oto kilka przykładowych zastosowań:

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

Zagadnienia:


To świetne rozwiązanie. Pamiętaj, aby wziąć pod uwagę również warunki PATCH i MSIPATCHREMOVE.
Garet Jax

W swojej tabeli prawdy miałeś na myśli użycie POPRZEDNIE WERSJI ZAINSTALOWANYCH zamiast UPGRADINGPRODUCTCODE, który jest używany przez ahmd0? Nie widzę żadnego odniesienia do PREVIOUSVERSIONSINSTALLED na stronie odniesienia MSI ( docs.microsoft.com/en-us/windows/win32/msi/property-reference ).
Patrick

Niektóre predykaty dla twoich właściwości nie uwzględniają wszystkich wierszy w tabeli ahmd0 (Zainstalowano, ZAINSTALUJ PONOWNIE, KOD PRODUKTU UPGRADING i USUŃ). Czy mógłbyś wyjaśnić dlaczego?
Patrick

0

Użyłem akcji niestandardowej oddzielnie zakodowanej w C ++ DLL i użyłem biblioteki DLL do wywołania odpowiedniej funkcji podczas odinstalowywania, używając następującej składni:

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

Korzystając z powyższego bloku kodu, mogłem uruchomić dowolną funkcję zdefiniowaną w C ++ DLL podczas odinstalowywania. FYI, moja funkcja dezinstalacji zawiera kod dotyczący czyszczenia bieżących danych użytkownika i wpisów rejestru.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.