Entfernen von Dateien bei der Deinstallation von WiX

84

Bei der Deinstallation meiner Anwendung möchte ich das Wix- Setup so konfigurieren , dass alle Dateien entfernt werden, die nach der ursprünglichen Installation hinzugefügt wurden . Es scheint, als würde das Deinstallationsprogramm nur die Verzeichnisse und Dateien entfernen, die ursprünglich aus der MSI-Datei installiert wurden, und alles andere, was später im Anwendungsordner hinzugefügt wurde, belassen. Mit anderen Worten, ich möchte das Verzeichnis bei der Deinstallation löschen. Wie mache ich das?

Pribeiro
quelle

Antworten:

83

Verwenden Sie das RemoveFile-Element mit On = " deinstallieren ". Hier ist ein Beispiel:

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

Aktualisieren

Es hat nicht 100% funktioniert. Die Dateien wurden entfernt, jedoch wurde keines der zusätzlichen Verzeichnisse - die nach der Installation erstellt wurden - entfernt. Irgendwelche Gedanken dazu? - Pribeiro

Leider unterstützt Windows Installer das Löschen von Verzeichnissen mit Unterverzeichnissen nicht. In diesem Fall müssen Sie auf benutzerdefinierte Aktionen zurückgreifen. Wenn Sie wissen, was Unterordner sind, erstellen Sie eine Reihe von RemoveFolder- und RemoveFile-Elementen.

Pavel Chuchuva
quelle
1
Danke Pavel. Es hat jedoch nicht 100% funktioniert. Die Dateien wurden entfernt, jedoch wurde keines der zusätzlichen Verzeichnisse - die nach der Installation erstellt wurden - entfernt. Irgendwelche Gedanken dazu?
Pribeiro
Oh, weder die Dateien unter diesen Verzeichnissen wurden gelöscht.
Pribeiro
Wenn Sie Dateien (z. B. Konfigurationsdateien) bei größeren Upgrades in 'MyAppFolder' aufbewahren, treten Probleme mit diesem Ansatz auf. Alle Dateien werden durch ein Upgrade entfernt.
Simon
1
Ist es mit Ihrem Code möglich, ein Verzeichnis in MyAppFolder zu erstellen? Wenn ich ein Verzeichnis hinzufüge, nachdem </Component>eine Kompilierung fehlgeschlagen istFound orphaned Component 'MyAppFolder'.
A.Pissicat
2
@PhilipRego Informationen zur CommonAppDataFolder-Dokumentation finden Sie unter msdn.microsoft.com/en-us/library/windows/desktop/aa367992.aspx.
Pavel Chuchuva
30

Verwenden Sie das RemoveFolderExElement der Util-Erweiterung in WiX.
Bei diesem Ansatz werden auch alle Unterverzeichnisse entfernt (im Gegensatz zur direkten Verwendung von RemoveFileElementen ). Dieses Element fügt der MSI-Datenbank temporäre Zeilen RemoveFileund RemoveFolderTabellen hinzu .

Alexey Ivanov
quelle
2
Warnung: Bei Verwendung von RemoveFolderEx on = "deinstallieren" wird auch der Ordner bei einem Upgrade (Wix 3.9) entfernt. Gleiches Verhalten bei RemoveFileund RemoveFolder. Wenn Sie Dateien während eines Upgrades behalten möchten, können Sie nicht alle diese Ansätze verwenden.
Simon
@ Simon Welchen Ansatz würden Sie vorschlagen, wenn Sie die Dateien bei einem Upgrade behalten möchten?
Bijington
@Bijington: Wenn Sie bei einem Upgrade bestimmte Dateien in Ihrem Installationsordner behalten möchten, verwenden Sie eine benutzerdefinierte Aktion, die benutzerdefinierten Code ausführt (z. B. c # geschriebene HandleSetup.exe). Der benutzerdefinierte Code liefert Ihre Dateien bei der Installation, behält Ihre Dateien beim Upgrade bei und entfernt die Dateien bei der Deinstallation.
Simon
@ Simon danke, ich muss möglicherweise diesen Ansatz untersuchen, obwohl dieser derzeit funktioniert: stackoverflow.com/a/21383113/32348
Bijington
13

Zu diesem Zweck habe ich einfach eine benutzerdefinierte Aktion erstellt, die beim Deinstallieren aufgerufen werden soll.

Der WiX-Code sieht folgendermaßen aus:

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

Der Code für die OnBeforeUninstall-Methode in InstallerCustomActions.DLL sieht folgendermaßen aus (in 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
Freund von George
quelle
11

Hier ist eine Variation von @ trondas Vorschlag. Ich lösche eine Datei "install.log", die während der Deinstallation von einer anderen benutzerdefinierten Aktion erstellt wird:

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

Soweit ich weiß, kann ich "RemoveFile" nicht verwenden, da diese Datei nach der Installation erstellt wird und nicht Teil einer Komponentengruppe ist.

Pierre
quelle
4
Ich habe diese Lösung mit einigen Änderungen verwendet, um das gesamte Verzeichnis zu entfernen: ExeCommand = "cmd / C RD" [INSTALLFOLDER] "/ s / q"
Dennis
@Dennis, wie man INSTALLFOLDER löscht, unter Win 10 wird es gelöscht, auf Windows Server 2012 jedoch nicht.
Eomeroff
Tolle Lösung. Vielen Dank!
Yurii Komarnytskyi
Ich habe eine Reihe von Dingen ausprobiert - man würde nicht glauben, dass es so schwierig sein könnte, eine einzelne Datei während der Deinstallation zu entfernen. Das hat bei mir aber funktioniert - danke!
Lucky Luke
7

Dies wäre eine vollständigere Antwort auf den @ Pavel- Vorschlag, für mich funktioniert es zu 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>

Und unter Produktelement:

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

Bei diesem Ansatz wird ein Registrierungswert mit dem gewünschten Pfad des Ordners festgelegt, der bei der Deinstallation gelöscht werden soll. Am Ende werden sowohl INSTALLFOLDER als auch der Registrierungsordner aus dem System entfernt. Beachten Sie, dass sich der Pfad zur Registrierung an einer anderen Struktur und an anderen Orten befinden kann.

Eli
quelle
6

Kein WIX-Experte, aber könnte eine mögliche (einfachere?) Lösung darin bestehen, die benutzerdefinierte Aktion "Quiet Execution" auszuführen, die Teil der integrierten Erweiterungen von WIX ist?

Könnte den Befehl rmdir MS DOS mit den Optionen / S und / Q ausführen.

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

Und die benutzerdefinierte Aktion, die die Arbeit erledigt, ist einfach:

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

Dann müssen Sie die InstallExecuteSequence wie an vielen Stellen dokumentiert ändern.

Update: Hatte Probleme mit diesem Ansatz. Am Ende wurde stattdessen eine benutzerdefinierte Aufgabe erstellt, dies ist jedoch immer noch eine praktikable Lösung, ohne dass die Details funktionieren.

Tronda
quelle
Ich mag diese Option, abgesehen von der Tatsache, dass Sie die cmd.exe in das Installationsprogramm aufnehmen. Sicherlich wird es jeder Computer haben, Sie müssen nur eine DirectorySearch verwenden, um es zu finden! :)
caveman_dick
4
Tu das nicht. 1) Sie binden cmd.exein Ihr Installationsprogramm ein. 2) Sie nehmen während der Skriptgenerierung Änderungen am System vor. 3) Es gibt keine Rollback-Option. 4) Behandelt gesperrte Dateien nicht korrekt
Nick Whaley,
Ich habe Zweifel, dass es legal ist, eine Datei aus der Windows-Installation zu verteilen. Es ist auch unklar, ob es auf dem Zielsystem funktioniert, auf dem möglicherweise eine andere Windows-Version ausgeführt wird.
Paul B.