Как добавить пользовательское действие WiX, которое происходит только при удалении (через MSI)?


161

Я хотел бы изменить установщик MSI (созданный через WiX ), чтобы удалить весь каталог при удалении.

Я понимаю параметры RemoveFileи RemoveFolderв WiX, но они недостаточно надежны, чтобы рекурсивно удалить всю папку, содержимое которой было создано после установки.

Я заметил похожий вопрос переполнения стека Удаление файлов при удалении WiX , но мне было интересно, можно ли это сделать проще, используя вызов пакетного сценария для удаления папки.

Это мой первый раз, когда я использую WiX, и я все еще изучаю пользовательские действия . Что будет основным примером настраиваемого действия, которое будет запускать пакетный скрипт при удалении?

Ответы:


188

РЕДАКТИРОВАТЬ : Возможно, посмотрите на ответ в настоящее время непосредственно ниже .


Эта тема долгое время была головной болью. Я наконец-то понял. В Интернете есть несколько решений, но ни одно из них не работает. И, конечно, нет документации. Таким образом, в таблице ниже есть несколько свойств, которые предлагается использовать, и значения, которые они имеют для различных сценариев установки:

альтернативный текст

Поэтому в моем случае я хотел, чтобы ЦС работал только при деинсталляции, а не при обновлении, не исправлении или модификации. В соответствии с таблицей выше я должен был использовать

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

И это сработало!


25
Верны ли значения в этой таблице? Зачем вам нужно добавить REMOVE = "ALL"? NOT UPGRADINGPRODUCTCODE имеет значение true только для удаления (согласно диаграмме), поэтому (NOT UPGRADINGPRODUCTCODE) AND (REMOVE = "ALL") также будет иметь значение true только для удаления. REMOVE = "ALL" кажется ненужным.
— Тодд Ропог

2
Я согласен с @ToddRopog - пример и таблица истинности, похоже, не согласны. Это действительно правильно?
— Тим Лонг

19
Таблица правды немного ошибочна. NOT UPGRADINGPRODUCTCODE верно и для первой установки
— Нил

2
Общие условия: alekdavis.blogspot.ru/2013/05/…
— KindDragon

1
Пожалуйста, подтвердите: Установлено и Установлено - это разные вещи, только Установлено устанавливается Установщиком Windows. Я не думаю, что УСТАНОВЛЕННЫЙ работает.
— Миха Виденманн

140

Есть много проблем с ответом yaluna , также имена свойств чувствительны к регистру, Installedэто правильное написание ( INSTALLEDне будет работать). Таблица выше должна была быть такой:

введите описание изображения здесь

Также при условии полного восстановления и удаления фактические значения свойств могут быть:

введите описание изображения здесь

Документация по синтаксису выражений WiX гласит:

В этих выражениях вы можете использовать имена свойств (помните, что они чувствительны к регистру).

Свойства задокументированы в Руководстве по установке Windows (например, Установлено )

РЕДАКТИРОВАТЬ: небольшая коррекция к первой таблице; очевидно, «удаление» может произойти и с REMOVEсамим собой True.


3
УДАЛИТЬ также, кажется, установлен для Изменения
— szx

2
Колонка «Обновление», это во время последовательности удаления старой версии или последовательности установки новой версии?
— Ник Уэйли

1
@NickWhaley: я давно не изучал этот вопрос, но я считаю, что опция «Обновить» доступна только при установке версии, которая больше уже установленной.
— ahmd0

1
@ ahmd0, ну конечно. Но в RemoveExistingProducts есть вложенная установка, которая имеет совершенно другой набор свойств. Это то, что находится в вашей колонке «Обновление». Остальная часть обновления идентична столбцу «Установка».
— Ник Уэйли

1
@NickWhaley: параметр REMOVE будет действительным для основных обновлений, т. Е. С 1.0.0 до 2.0.0, а не с 1.0.0 до 1.1.0, во время выполнения программы удаления предыдущей версии. Чтобы запустить настраиваемое действие во время крупного обновления в установке новых версий, вам нужно сослаться на ActionProperty, определенный в таблице MSI обновления для обновления этой версии. symantec.com/connect/articles/msi-upgrade-overview msdn.microsoft.com/en-us/library/aa372379%28v=vs.85%29.aspx
— Chaoix

48

Вы можете сделать это с помощью специального действия. Вы можете добавить ссылку на свое пользовательское действие в разделе <InstallExecuteSequence>:

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

Тогда вам также нужно будет определить свое действие в разделе <Product>:

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

Где FileCleanerEXE - это двоичный файл (в моем случае это небольшая программа на С ++, выполняющая пользовательское действие), которая также определяется в разделе <Product>:

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

Реальная хитрость в этом заключается в Installed AND NOT UPGRADINGPRODUCTCODEусловии настраиваемого действия, при котором ваше действие будет запускаться при каждом обновлении (поскольку обновление действительно является деинсталляцией, а затем переустановите). Что, если вы удаляете файлы, вероятно, не хотите, чтобы вы хотели во время обновления.

С другой стороны: я рекомендую решить проблему с использованием чего-то вроде программы на C ++ для выполнения действия вместо пакетного скрипта из-за его мощности и контроля - и вы можете предотвратить мигание окна «cmd prompt» во время Ваш установщик работает.


3
25 голосов, но не принятый ответ. Добро пожаловать в мир установщиков! :)
— Кристофер Пэйнтер

4
Это на самом деле не работает. Когда вы захотите выполнить файл fileCleaner.exe, который установлен в вашей собственной папке установки, это будет проблемой "курица и яйцо": CustomActionбудет выполнено "After = 'InstallFinalize'". На этом этапе все файлы удаляются из папки установки. Также файл Cleaner.exe. Таким образом, вы не можете выполнить его через CustomAction. Этот ответ просто неправильный. Мне интересно про 42 голоса!
— Симон

40

Самая большая проблема с пакетным скриптом - обработка отката, когда пользователь нажимает кнопку отмены (или что-то идет не так во время вашей установки). Правильный способ обработки этого сценария - создать CustomAction, который добавляет временные строки в таблицу RemoveFiles. Таким образом, установщик Windows обрабатывает случаи отката для вас. Это безумно проще, когда вы видите решение.

В любом случае, чтобы действие выполнялось только во время удаления, добавьте элемент Condition с помощью:

REMOVE ~= "ALL"

~ = говорит, что сравнивать без учета регистра (хотя я думаю, что ALL всегда uppercaesd). См. Документацию MSI SDK о синтаксисе условий для получения дополнительной информации.

PS: Никогда не было случая, чтобы я сел и подумал: «О, пакетный файл был бы хорошим решением в установочном пакете». На самом деле, поиск установочного пакета, в котором есть пакетный файл, только побудил бы меня вернуть продукт за возмещение.


Я собирался использовать пакетный скрипт, а затем увидел раздел PS. Спасибо за то, что спасли меня :) У меня работает Remove ~ = "ALL".
— ArNumb

12

Вот набор свойств, которые я сделал, которые более интуитивно понятны, чем встроенные. Условия основаны на таблице истинности, представленной выше 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>

Вот пример использования:

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

Вопросы:

  • UPGRADINGPRODUCTCODE устанавливается во время действия RemoveExistingProducts, поэтому любые пользовательские действия, которые вы выполняете ранее, не будут знать, что это обновление https://docs.microsoft.com/en-us/windows/desktop/Msi/upgradingproductcode

Это отличное решение. Не забудьте также рассмотреть условия PATCH и MSIPATCHREMOVE.
— Гарет Джакс

В своей таблице истинности вы имели в виду использовать PREVIOUSVERSIONSINSTALLED вместо UPGRADINGPRODUCTCODE, который используется ahmd0? Я не вижу ссылок на PREVIOUSVERSIONSINSTALLED на странице справки по свойствам MSI ( docs.microsoft.com/en-us/windows/win32/msi/property-reference ).
— Патрик

Некоторые из предикатов для ваших свойств не учитывают все строки таблицы ahmd0 (Установлено, REINSTALL, UPGRADINGPRODUCTCODE и REMOVE). Не могли бы вы объяснить, почему?
— Патрик

0

Я использовал Custom Action, отдельно закодированный в C ++ DLL, и использовал DLL для вызова соответствующей функции при деинсталляции, используя этот синтаксис:

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

Используя приведенный выше блок кода, я смог запустить любую функцию, определенную в C ++ DLL, при удалении. К вашему сведению, моя функция удаления имела код, касающийся очистки текущих пользовательских данных и записей реестра.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.