Удаление файлов при удалении WiX

84

При удалении моего приложения я хотел бы настроить Wix так , чтобы удалить все файлы, которые были добавлены после первоначальной установки . Похоже, что деинсталлятор удаляет только каталоги и файлы, которые были изначально установлены из файла MSI, и оставляет все остальное, что было добавлено позже, в папке приложения. Другими словами, я хотел бы очистить каталог при удалении. Как я могу это сделать?

прибейро
источник

Ответы:

83

Используйте RemoveFile элемент с On = « удалить ». Вот пример:

<Directory Id="CommonAppDataFolder" Name="CommonAppDataFolder">
  <Directory Id="MyAppFolder" Name="My">
    <Component Id="MyAppFolder" Guid="*">
      <CreateFolder />
      <RemoveFile Id="PurgeAppFolder" Name="*.*" On="uninstall" />
    </Component>
  </Directory>
</Directory>

Обновить

Не получилось на 100%. Он удалил файлы, однако ни один из дополнительных каталогов, созданных после установки, не был удален. Есть мысли по этому поводу? - прибейро

К сожалению, установщик Windows не поддерживает удаление каталогов с подкаталогами. В этом случае вам придется прибегнуть к индивидуальному действию. Или, если вы знаете, что такое подпапки, создайте группу элементов RemoveFolder и RemoveFile.

Павел Чучува
источник
1
Спасибо, Павел. Однако на 100% это не сработало. Он удалил файлы, однако ни один из дополнительных каталогов, созданных после установки, не был удален. Есть мысли по этому поводу?
pribeiro
О, ни файлы в этих каталогах не были удалены.
pribeiro
Когда вы будете хранить файлы (например, файлы конфигурации) в папке MyAppFolder при основных обновлениях, у вас возникнут проблемы с этим подходом. Все файлы будут удалены при обновлении.
Саймон
1
Можно ли с помощью вашего кода создать каталог в MyAppFolder? Когда я добавляю каталог после </Component>Found orphaned Component 'MyAppFolder'.
сбоя
2
@PhilipRego См. Документацию по CommonAppDataFolder на msdn.microsoft.com/en-us/library/windows/desktop/aa367992.aspx.
Павел Чучува 08
30

Используйте RemoveFolderExэлемент из расширения Util в WiX.
При таком подходе все подкаталоги также удаляются (в отличие от использования RemoveFileelement напрямую ). Этот элемент добавляет временные строки RemoveFileи RemoveFolderтаблицу в базу данных MSI.

Алексей Иванов
источник
2
Предупреждение: при использовании RemoveFolderEx on = "uninstall" папка также удаляется при обновлении (Wix 3.9). Такое же поведение на RemoveFileи RemoveFolder. Если вы хотите сохранить файлы при обновлении, вы не можете использовать все эти подходы.
Саймон
@Simon, какой подход вы бы предложили, если хотите сохранить файлы при обновлении?
Bijington
@Bijington: если вы хотите сохранить определенные файлы в папке установки при обновлении, используйте настраиваемое действие, которое выполняет настраиваемый код (например, HandleSetup.exe, написанный на C #). Пользовательский код доставляет ваши файлы при установке, сохраняет файлы при обновлении и удаляет файлы при удалении.
Саймон
@Simon, спасибо, мне, возможно, придется изучить этот подход, хотя он в настоящее время работает: stackoverflow.com/a/21383113/32348
Bijington
13

Для этого я просто создал настраиваемое действие, которое будет вызываться при удалении.

Код WiX будет выглядеть так:

<Binary Id="InstallUtil" src="InstallUtilLib.dll" />

<CustomAction Id="DIRCA_TARGETDIR" Return="check" Execute="firstSequence" Property="TARGETDIR" Value="[ProgramFilesFolder][Manufacturer]\[ProductName]" />
<CustomAction Id="Uninstall" BinaryKey="InstallUtil" DllEntry="ManagedInstall" Execute="deferred" />
<CustomAction Id="UninstallSetProp" Property="Uninstall" Value="/installtype=notransaction /action=uninstall /LogFile= /targetDir=&quot;[TARGETDIR]\Bin&quot; &quot;[#InstallerCustomActionsDLL]&quot; &quot;[#InstallerCustomActionsDLLCONFIG]&quot;" />

<Directory Id="BinFolder" Name="Bin" >
    <Component Id="InstallerCustomActions" Guid="*">
        <File Id="InstallerCustomActionsDLL" Name="SetupCA.dll" LongName="InstallerCustomActions.dll" src="InstallerCustomActions.dll" Vital="yes" KeyPath="yes" DiskId="1" Compressed="no" />
        <File Id="InstallerCustomActionsDLLCONFIG" Name="SetupCA.con" LongName="InstallerCustomActions.dll.Config" src="InstallerCustomActions.dll.Config" Vital="yes" DiskId="1" />
    </Component>
</Directory>

<Feature Id="Complete" Level="1" ConfigurableDirectory="TARGETDIR">
    <ComponentRef Id="InstallerCustomActions" />
</Feature>

<InstallExecuteSequence>
    <Custom Action="UninstallSetProp" After="MsiUnpublishAssemblies">$InstallerCustomActions=2</Custom>
    <Custom Action="Uninstall" After="UninstallSetProp">$InstallerCustomActions=2</Custom>
</InstallExecuteSequence>

Код метода OnBeforeUninstall в InstallerCustomActions.DLL будет выглядеть следующим образом (в VB).

Protected Overrides Sub OnBeforeUninstall(ByVal savedState As System.Collections.IDictionary)
    MyBase.OnBeforeUninstall(savedState)

    Try
        Dim CommonAppData As String = Me.Context.Parameters("CommonAppData")
        If CommonAppData.StartsWith("\") And Not CommonAppData.StartsWith("\\") Then
            CommonAppData = "\" + CommonAppData
        End If
        Dim targetDir As String = Me.Context.Parameters("targetDir")
        If targetDir.StartsWith("\") And Not targetDir.StartsWith("\\") Then
            targetDir = "\" + targetDir
        End If

        DeleteFile("<filename.extension>", targetDir) 'delete from bin directory
        DeleteDirectory("*.*", "<DirectoryName>") 'delete any extra directories created by program
    Catch
    End Try
End Sub

Private Sub DeleteFile(ByVal searchPattern As String, ByVal deleteDir As String)
    Try
        For Each fileName As String In Directory.GetFiles(deleteDir, searchPattern)
            File.Delete(fileName)
        Next
    Catch
    End Try
End Sub

Private Sub DeleteDirectory(ByVal searchPattern As String, ByVal deleteDir As String)
    Try
        For Each dirName As String In Directory.GetDirectories(deleteDir, searchPattern)
            Directory.Delete(dirName)
        Next
    Catch
    End Try
End Sub
Друг Джорджа
источник
11

Вот вариант предложения @tronda. Я удаляю файл install.log, который создается другим настраиваемым действием во время удаления:

<Product>
    <CustomAction Id="Cleanup_logfile" Directory="INSTALLFOLDER"
    ExeCommand="cmd /C &quot;del install.log&quot;"
    Execute="deferred" Return="ignore" HideTarget="no" Impersonate="no" />

    <InstallExecuteSequence>
      <Custom Action="Cleanup_logfile" Before="RemoveFiles" >
        REMOVE="ALL"
      </Custom>
    </InstallExecuteSequence>
</Product>

Насколько я понимаю, я не могу использовать RemoveFile, потому что этот файл создается после установки и не является частью группы компонентов.

Пьер
источник
4
Я действительно использовал это решение, с некоторыми изменениями для удаления всего каталога: ExeCommand = "cmd / C RD" [INSTALLFOLDER] "/ s / q"
Деннис
@Dennis, как удалить INSTALLFOLDER, в win 10 он удаляется, но на сервере Windows 2012 этого не происходит.
eomeroff
Отличное решение. Благодаря!
Юрий Комарницкий
Я пробовал ряд вещей - никто не мог подумать, что удалить один файл во время деинсталляции может быть так сложно. Это сработало для меня - спасибо!
Лаки Люк,
7

Это был бы более полный ответ на предложение @Pavel , для меня он работает на 100%:

<Fragment Id="FolderUninstall">
    <?define RegDir="SYSTEM\ControlSet001\services\[Manufacturer]:[ProductName]"?>
    <?define RegValueName="InstallDir"?>
    <Property Id="INSTALLFOLDER">
        <RegistrySearch Root="HKLM" Key="$(var.RegDir)" Type="raw" 
                  Id="APPLICATIONFOLDER_REGSEARCH" Name="$(var.RegValueName)" />
    </Property>

    <DirectoryRef Id='INSTALLFOLDER'>
        <Component Id="UninstallFolder" Guid="*">
            <CreateFolder Directory="INSTALLFOLDER"/>
            <util:RemoveFolderEx Property="INSTALLFOLDER" On="uninstall"/>
            <RemoveFolder Id="INSTALLFOLDER" On="uninstall"/>
            <RegistryValue Root="HKLM" Key="$(var.RegDir)" Name="$(var.RegValueName)" 
                    Type="string" Value="[INSTALLFOLDER]" KeyPath="yes"/>
        </Component>
    </DirectoryRef>
</Fragment>

И в элементе Product:

<Feature Id="Uninstall">
    <ComponentRef Id="UninstallFolder" Primary="yes"/>
</Feature>

Этот подход устанавливает значение реестра с желаемым путем к папке, которая будет удалена при удалении. В конце и INSTALLFOLDER, и папка реестра удаляются из системы. Обратите внимание, что путь к реестру может находиться в другом улье и других местах.

Эли
источник
6

Не эксперт WIX, но может ли возможным (более простым?) Решением этой проблемы запустить настраиваемое действие Quiet Execution, которое является частью встроенных расширений WIX?

Можно запустить команду rmdir MS DOS с параметрами / S и / Q.

<Binary Id="CommandPrompt" SourceFile="C:\Windows\System32\cmd.exe" />

А настраиваемое действие, выполняющее эту работу, простое:

<CustomAction Id="DeleteFolder" BinaryKey="CommandPrompt" 
              ExeCommand='/c rmdir /S /Q "[CommonAppDataFolder]MyAppFolder\PurgeAppFolder"' 
              Execute="immediate" Return="check" />

Затем вам придется изменить InstallExecuteSequence, как описано во многих местах.

Обновление: были проблемы с этим подходом. Вместо этого закончил создание настраиваемой задачи, но по-прежнему считает это жизнеспособным решением, но не заставляет детали работать.

тронда
источник
Мне нравится эта опция, исключая тот факт, что вы включаете cmd.exe в программу установки. Конечно, он есть на каждой машине, вам просто нужно использовать DirectorySearch, чтобы найти его! :)
caveman_dick 08
4
Не делай этого. 1) вы встраиваете cmd.exeв свой установщик. 2) Вы вносите изменения в систему во время генерации скрипта 3) Нет возможности отката 4) Некорректно работает с заблокированными файлами
Ник Уэйли
Сомневаюсь, что распространение файла из установки Windows законно. Также неясно, будет ли он работать в целевой системе, которая может работать с другой версией Windows.
Пол Б.
Файлы не распространялись. Он использует файлы, установленные в ОС.
tronda