Usuwanie plików podczas odinstalowywania WiX

84

Podczas odinstalowywania mojej aplikacji chciałbym skonfigurować instalację Wix , aby usunąć wszystkie pliki, które zostały dodane po pierwotnej instalacji . Wygląda na to, że deinstalator usuwa tylko katalogi i pliki, które zostały pierwotnie zainstalowane z pliku MSI i pozostawia wszystko inne, co zostało dodane później w folderze aplikacji. Innymi słowy, chciałbym wyczyścić katalog podczas odinstalowywania. Jak mogę to zrobić?

pribeiro
źródło

Odpowiedzi:

83

Użyj elementu RemoveFile z On = " uninstall ". Oto przykład:

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

Aktualizacja

Nie działało w 100%. Usunął pliki, jednak żaden z dodatkowych katalogów - utworzonych po instalacji - nie został usunięty. Jakieś przemyślenia na ten temat? - pribeiro

Niestety Instalator Windows nie obsługuje usuwania katalogów z podkatalogami. W takim przypadku musisz skorzystać z niestandardowej akcji. Lub, jeśli wiesz, co to są podfoldery, utwórz kilka elementów RemoveFolder i RemoveFile.

Pavel Chuchuva
źródło
1
Dziękuję Pavel. Jednak to nie działało w 100%. Usunął pliki, jednak żaden z dodatkowych katalogów - utworzonych po instalacji - nie został usunięty. Jakieś przemyślenia na ten temat?
pribeiro
Och, żadne pliki w tych katalogach nie zostały usunięte.
pribeiro
Jeśli będziesz przechowywać pliki (na przykład pliki konfiguracyjne) w 'MyAppFolder' w przypadku głównych aktualizacji, będziesz mieć problemy z tym podejściem. Wszystkie pliki zostaną usunięte podczas aktualizacji.
Simon
1
Czy za pomocą Twojego kodu można utworzyć katalog w MyAppFolder? Kiedy dodam katalog po </Component>niepowodzeniu kompilacjiFound orphaned Component 'MyAppFolder'.
A.Pissicat
2
@PhilipRego Zobacz msdn.microsoft.com/en-us/library/windows/desktop/aa367992.aspx, aby uzyskać dokumentację CommonAppDataFolder.
Pavel Chuchuva
30

Użyj RemoveFolderExelementu z rozszerzenia Util w WiX.
Przy takim podejściu wszystkie podkatalogi są również usuwane (w przeciwieństwie do bezpośredniego używania RemoveFileelementu ). Ten element dodaje tymczasowe wiersze RemoveFilei RemoveFoldertabelę w bazie danych MSI.

Aleksiej Iwanow
źródło
2
Ostrzeżenie: Używając RemoveFolderEx on = "uninstall", usuwa również folder podczas aktualizacji (Wix 3.9). To samo zachowanie w RemoveFilei RemoveFolder. Jeśli chcesz zachować pliki po uaktualnieniu, nie możesz użyć wszystkich tych podejść.
Simon,
@Simon Jakie podejście byś zaproponował, gdybyś chciał zachować pliki po uaktualnieniu?
Bijington,
@Bijington: Jeśli chcesz zachować określone pliki w folderze instalacyjnym podczas aktualizacji, użyj niestandardowej akcji, która wykonuje niestandardowy kod (np. HandleSetup.exe napisany w C #). Kod niestandardowy dostarcza pliki podczas instalacji, utrzymując je podczas aktualizacji i usuwa je podczas odinstalowywania.
Simon,
@Simon dzięki, być może będę musiał przyjrzeć się temu podejściu, chociaż to obecnie działa: stackoverflow.com/a/21383113/32348
Bijington
13

Aby to zrobić, po prostu utworzyłem niestandardową akcję, która będzie wywoływana podczas odinstalowywania.

Kod WiX będzie wyglądał następująco:

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

Kod metody OnBeforeUninstall w InstallerCustomActions.DLL będzie wyglądał następująco (w języku 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
Przyjaciel George'a
źródło
11

Oto wariacja na temat sugestii @ tronda. Usuwam plik „install.log”, który jest tworzony przez inną akcję niestandardową podczas odinstalowywania:

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

O ile rozumiem, nie mogę użyć opcji „RemoveFile”, ponieważ ten plik jest tworzony po instalacji i nie jest częścią grupy komponentów.

Pierre
źródło
4
Użyłem tego rozwiązania, z pewnymi zmianami, aby usunąć cały katalog: ExeCommand = "cmd / C RD" [INSTALLFOLDER] "/ s / q"
Dennis
@ Dennis, jak usunąć INSTALLFOLDER, na win 10 zostaje usunięty, ale na serwerze Windows 2012 tak nie jest.
eomeroff
Świetne rozwiązanie. Dzięki!
Yurii Komarnytskyi
Próbowałem wielu rzeczy - nie można by pomyśleć, że usunięcie pojedynczego pliku podczas odinstalowywania może być takie trudne. Jednak to zadziałało - dzięki!
Lucky Luke
7

To byłaby pełniejsza odpowiedź na sugestię @Pavel , dla mnie działa w 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>

A pod elementem Produkt:

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

Ta metoda ustawia wartość rejestru z żądaną ścieżką folderu, który ma zostać usunięty podczas odinstalowywania. Na koniec zarówno INSTALLFOLDER, jak i folder rejestru są usuwane z systemu. Zauważ, że ścieżka do rejestru może znajdować się w innej gałęzi i innych lokalizacjach.

Eli
źródło
6

Nie jesteś ekspertem WIX, ale czy możliwym (prostszym?) Rozwiązaniem tego problemu może być uruchomienie niestandardowej akcji cichej realizacji, która jest częścią wbudowanych rozszerzeń WIX?

Można uruchomić polecenie rmdir MS DOS z opcjami / S i / Q.

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

Niestandardowe działanie wykonujące zadanie jest proste:

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

Następnie będziesz musiał zmodyfikować InstallExecuteSequence zgodnie z dokumentacją w wielu miejscach.

Aktualizacja: Wystąpiły problemy z tym podejściem. Skończyło się na utworzeniu niestandardowego zadania, ale nadal uważa to za wykonalne rozwiązanie, ale bez uzyskania szczegółów.

tronda
źródło
Podoba mi się ta opcja poza faktem, że dołączasz cmd.exe do instalatora. Z pewnością każdy komputer będzie go posiadał, wystarczy skorzystać z usługi DirectorySearch, aby go znaleźć! :)
caveman_dick
4
Nie rób tego. 1) osadzasz cmd.exew swoim instalatorze. 2) Wprowadzasz zmiany w systemie podczas generowania skryptu 3) Nie ma opcji wycofywania 4) Nie radzi sobie poprawnie z zablokowanymi plikami
Nick Whaley
Mam wątpliwości, czy rozpowszechnianie pliku z instalacji systemu Windows jest legalne. Nie jest również jasne, czy będzie działać w systemie docelowym, na którym może działać inna wersja systemu Windows.
Paul B.
Żadne pliki nie zostały rozpowszechnione. Używa plików zainstalowanych w systemie operacyjnym.
tronda